aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNetheriteMiner <88792142+NetheriteMiner@users.noreply.github.com>2023-08-28 17:43:25 -0400
committerGitHub <noreply@github.com>2023-08-28 23:43:25 +0200
commit79aa4c9575d9b374af60781265b1dc75e19355e6 (patch)
tree7accfd1a6f3afccea97ec373e99642ba936d84f3
parente53b7e6b49244085eed7623fa497988529aa5414 (diff)
downloadskyhanni-79aa4c9575d9b374af60781265b1dc75e19355e6.tar.gz
skyhanni-79aa4c9575d9b374af60781265b1dc75e19355e6.tar.bz2
skyhanni-79aa4c9575d9b374af60781265b1dc75e19355e6.zip
Add Translator (#383)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt1
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt12
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/ChatConfig.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt218
4 files changed, 236 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index e428782df..c16a19a5c 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -381,6 +381,7 @@ class SkyHanniMod {
loadModule(WatchdogHider())
loadModule(AccountUpgradeReminder())
loadModule(PetExpTooltip())
+ loadModule(Translator())
init()
diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
index 0768e9f65..ab94a1e6d 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
@@ -7,6 +7,7 @@ import at.hannibal2.skyhanni.data.ChatManager
import at.hannibal2.skyhanni.data.GuiEditManager
import at.hannibal2.skyhanni.features.bingo.BingoCardDisplay
import at.hannibal2.skyhanni.features.bingo.BingoNextStepHelper
+import at.hannibal2.skyhanni.features.chat.Translator
import at.hannibal2.skyhanni.features.event.diana.BurrowWarpHelper
import at.hannibal2.skyhanni.features.event.diana.InquisitorWaypointShare
import at.hannibal2.skyhanni.features.fame.AccountUpgradeReminder
@@ -142,6 +143,13 @@ object Commands {
"shfarmingprofile",
"Look up the farming profile from yourself or another player on elitebot.dev"
) { FarmingWeightDisplay.lookUpCommand(it) }
+ registerCommand(
+ "shcopytranslation",
+ "<language code (2 letters)> <messsage to translate>\n" +
+ "Requires the Chat > Translator feature to be enabled.\n" +
+ "Copies the translation for a given message to your clipboard. " +
+ "Language codes are at the end of the translation when you click on a message."
+ ) { Translator.fromEnglish(it) }
}
private fun usersBugFix() {
@@ -237,6 +245,10 @@ object Commands {
registerCommand("shstopcityprojectreminder", "") { CityProjectFeatures.disable() }
registerCommand("shsendcontests", "") { GardenNextJacobContest.shareContestConfirmed(it) }
registerCommand("shstopaccountupgradereminder", "") { AccountUpgradeReminder.disable() }
+ registerCommand(
+ "shsendtranslation",
+ "Respond with a translation of the message that the user clicks"
+ ) { Translator.toEnglish(it) }
}
private fun commandHelp(args: Array<String>) {
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/ChatConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/ChatConfig.java
index 30bba65bd..ee46054f6 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/ChatConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/ChatConfig.java
@@ -133,4 +133,9 @@ public class ChatConfig {
@ConfigOption(name = "Arachne Hider", desc = "Hide chat messages about the Arachne Fight while outside of §eArachne's Sanctuary§7.")
@ConfigEditorBoolean
public boolean hideArachneMessages = false;
+
+ @Expose
+ @ConfigOption(name = "Translator", desc = "Click on a message to translate it into English. Use /shcopytranslation to get the translation from English. Translation is not guaranteed to be 100% accurate.")
+ @ConfigEditorBoolean
+ public boolean translator = false;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt
new file mode 100644
index 000000000..f39905876
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt
@@ -0,0 +1,218 @@
+package at.hannibal2.skyhanni.features.chat
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.test.command.CopyErrorCommand
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.OSUtils
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import com.google.gson.*
+import net.minecraft.event.ClickEvent
+import net.minecraft.event.HoverEvent
+import net.minecraft.util.ChatComponentText
+import net.minecraft.util.ChatStyle
+import net.minecraftforge.client.event.ClientChatReceivedEvent
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import org.apache.http.client.config.RequestConfig
+import org.apache.http.client.methods.HttpGet
+import org.apache.http.impl.client.HttpClientBuilder
+import org.apache.http.impl.client.HttpClients
+import org.apache.http.message.BasicHeader
+import org.apache.http.util.EntityUtils
+import java.net.URLDecoder
+import java.net.URLEncoder
+
+class Translator {
+
+ private val messageContentRegex = Regex(".*: (.*)")
+
+ // Logic for listening for a user click on a chat message is from NotEnoughUpdates
+
+ @SubscribeEvent(priority = EventPriority.LOWEST, receiveCanceled = true)
+ fun onGuiChat(e: ClientChatReceivedEvent) {
+ if (!SkyHanniMod.feature.chat.translator) return
+ if (e.type != 0.toByte()) return // If this is not a player-sent message, return
+
+ val chatComponent = e.message
+ // If you want to help me debug, experience the bug while this line is uncommented (spams logs)
+// consoleLog(chatComponent.toString())
+ val message = chatComponent.unformattedText
+ if (!messageContentRegex.matches(message.removeColor())) return
+
+ val clickStyle = createClickStyle(message)
+ chatComponent.setChatStyle(clickStyle)
+ }
+
+ private fun createClickStyle(message: String): ChatStyle {
+ val style = ChatStyle()
+ style.setChatClickEvent(
+ ClickEvent(
+ ClickEvent.Action.RUN_COMMAND,
+ "/shsendtranslation ${messageContentRegex.find(message.removeColor())!!.groupValues[1]}"
+ )
+ )
+ style.setChatHoverEvent(
+ HoverEvent(
+ HoverEvent.Action.SHOW_TEXT,
+ ChatComponentText("§bClick to translate!")
+ )
+ )
+ return style
+ }
+
+
+ companion object {
+ // Using my own getJSONResponse because of 1 line of difference.
+ private val parser = JsonParser()
+ private val builder: HttpClientBuilder =
+ HttpClients.custom().setUserAgent("SkyHanni/${SkyHanniMod.version}")
+ .setDefaultHeaders(
+ mutableListOf(
+ BasicHeader("Pragma", "no-cache"),
+ BasicHeader("Cache-Control", "no-cache")
+ )
+ )
+ .setDefaultRequestConfig(
+ RequestConfig.custom()
+ .build()
+ )
+ .useSystemProperties()
+
+ /*
+ * Simplified version of the JSON response:
+ * [
+ * [
+ * [
+ * 'translated sentence one with a space after the punctuation. '
+ * 'original sentence one without a space after the punctuation.'
+ * ],
+ * [
+ * 'translated sentence two without punctuation bc it's last'
+ * 'original sentence two without punctuation'
+ * ]
+ * ],
+ * null,
+ * 'target language as a two letter code following ISO 639-1',
+ * ]
+ */
+
+ private fun getJSONResponse(urlString: String, silentError: Boolean = false): JsonElement {
+ val client = builder.build()
+ try {
+ client.execute(HttpGet(urlString)).use { response ->
+ val entity = response.entity
+ if (entity != null) {
+ val retSrc = EntityUtils.toString(entity)
+ try {
+ return parser.parse(retSrc)
+ } catch (e: JsonSyntaxException) {
+ if (e.message?.contains("Use JsonReader.setLenient(true)") == true) {
+ println("MalformedJsonException: Use JsonReader.setLenient(true)")
+ println(" - getJSONResponse: '$urlString'")
+ LorenzUtils.debug("MalformedJsonException: Use JsonReader.setLenient(true)")
+ } else if (retSrc.contains("<center><h1>502 Bad Gateway</h1></center>")) {
+ e.printStackTrace()
+
+ } else {
+ CopyErrorCommand.logError(
+ Error("Hypixel API error for url: '$urlString'", e),
+ "Failed to load data from Hypixel API"
+ )
+ }
+ }
+ }
+ }
+ } catch (throwable: Throwable) {
+ if (silentError) {
+ throw throwable
+ } else {
+ CopyErrorCommand.logError(
+ Error("Hypixel API error for url: '$urlString'", throwable),
+ "Failed to load data from Hypixel API"
+ )
+ }
+ } finally {
+ client.close()
+ }
+ return JsonObject()
+ }
+
+ private fun getTranslationToEnglish(message: String): String {
+ val url =
+ "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=en&dt=t&q=" + URLEncoder.encode(
+ message,
+ "UTF-8"
+ )
+
+ var messageToSend = ""
+ val layer1 = getJSONResponse(url).asJsonArray
+ val language = layer1[2].toString()
+ if (language == "en") return "Unable to translate!"
+
+ val layer2 = layer1[0] as JsonArray
+
+ for (layer3 in layer2) {
+ val arrayLayer3 = layer3 as JsonArray
+ val sentence = arrayLayer3[0].toString()
+ val sentenceWithoutQuotes = sentence.substring(1, sentence.length - 1)
+ messageToSend = "$messageToSend$sentenceWithoutQuotes"
+ }
+ messageToSend = "$messageToSend §7(Language: $language)"
+
+ return URLDecoder.decode(messageToSend, "UTF-8").replace("\\", "")
+
+ }
+
+ private fun getTranslationFromEnglish(message: String, lang: String): String {
+ val url =
+ "https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=$lang&dt=t&q=" + URLEncoder.encode(
+ message,
+ "UTF-8"
+ )
+
+ val layer1 = getJSONResponse(url).asJsonArray
+ val layer2 = layer1[0] as JsonArray
+
+ val firstSentence = (layer2[0] as JsonArray).get(0).toString()
+ var messageToSend = firstSentence.substring(0, firstSentence.length - 1)
+ for (sentenceIndex in 1..<layer2.size()) {
+ val sentence = (layer2[sentenceIndex] as JsonArray).get(0).toString()
+ val sentenceWithoutQuotes = sentence.substring(1, sentence.length - 1)
+ messageToSend = "$messageToSend$sentenceWithoutQuotes"
+ } // The first translated sentence only has 1 extra char at the end, but sentences after it need 1 at the front and 1 at the end removed in the substring
+ messageToSend = messageToSend.substring(1, messageToSend.length - 1)
+ return URLDecoder.decode(messageToSend, "UTF-8").replace("\\", "")
+ }
+
+ fun toEnglish(args: Array<String>) {
+ if (!SkyHanniMod.feature.chat.translator) return
+ var message = ""
+ for (i in args) {
+ message = "$message$i "
+ }
+
+ val translation = getTranslationToEnglish(message)
+
+ if (translation == "Unable to translate!") LorenzUtils.chat("§c[SkyHanni] Unable to translate message :(")
+ else LorenzUtils.chat("§e[SkyHanni] Found translation: §f$translation")
+ }
+
+ fun fromEnglish(args: Array<String>) {
+ if (!SkyHanniMod.feature.chat.translator) return
+ if (args.size < 2 || args[0].length != 2) { // args[0] is the language code
+ LorenzUtils.chat("§cUsage: /shcopytranslation <two letter language code (at the end of a translation)> <message>")
+ return
+ }
+ val language = args[0]
+ var message = ""
+ for (i in 1..<args.size) {
+ message = "$message${args[i]} "
+ }
+
+ val translation = getTranslationFromEnglish(message, language)
+ LorenzUtils.chat("§6[SkyHanni] §eCopied translation to clipboard: $translation")
+ OSUtils.copyToClipboard(translation)
+ }
+ }
+
+} \ No newline at end of file