From 69dbeb523db6518e7e0242dba4644b4db67bf392 Mon Sep 17 00:00:00 2001 From: Ian William O'Neill Date: Mon, 1 Jul 2019 01:23:21 +0100 Subject: [PATCH] Chat Translator plugin (#824) * Initial push for Chat Translation Plugin. * Need help adding the 'Translate' menu option for translating selected Players messages. Finished outgoing message translation however if the user has been limited by Google for the likes of using the Public Chat translation in over populated and spammy areas such as the GE they will not be able to send a message for upto 5 minutes while the Player Chat Translation config is enabled. Need to think more about ways to handle this though once the selective translation has been completed we can safely remove the 'All' public chat messages part of the plugin to prevent users from being Limited by Google. * Clean-up for Travis. * Hidden the Public Chat Translation until a better implementation is added to prevent users from being accidentally locked out by Google. * Hidden the Public Chat Translation until a better implementation is added to prevent users from being accidentally locked out by Google. * Added config options for adding the 'Translate' right-click menu option in the Chatbox. (More so to hide it while the Public Chat Translate part of the plugin is disabled/hidden.) * Update ChatTranslationConfig.java --- runelite-client/pom.xml | 5 + .../ChatTranslationConfig.java | 78 ++++++ .../ChatTranslationPlugin.java | 236 ++++++++++++++++++ .../plugins/chattranslation/Languages.java | 24 ++ .../plugins/chattranslation/Translator.java | 46 ++++ 5 files changed, 389 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/Languages.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/Translator.java diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 70c5c83000..98b1c9529c 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -284,6 +284,11 @@ asm-all 6.0_BETA + + org.json + json + 20180813 + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationConfig.java new file mode 100644 index 0000000000..565aec82d8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationConfig.java @@ -0,0 +1,78 @@ +package net.runelite.client.plugins.chattranslation; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("chattranslation") +public interface ChatTranslationConfig extends Config +{ + + @ConfigItem( + keyName = "publicChat", + name = "Translate incoming Messages", + description = "Would you like to Translate Public Chat?", + position = 0, + group = "Public Chat Translation", + hidden = true + ) + default boolean publicChat() + { + return false; + } + + @ConfigItem( + keyName = "translateOptionVisable", + name = "Show 'Translate' menu option", + description = "Adds 'Translate' to the right-click menu in the Chatbox.", + position = 1, + group = "Public Chat Translation", + hidden = true +// unhide = "publicChat" + ) + default boolean translateOptionVisable() + { + return false; + } + + @ConfigItem( + keyName = "publicTargetLanguage", + name = "Target Language", + description = "Language to translate messages too.", + position = 2, + group = "Public Chat Translation", + hidden = true +// unhide = "publicChat" + ) + default Languages publicTargetLanguage() + { + return Languages.ENGLISH; + } + + @ConfigItem( + keyName = "playerChat", + name = "Translate outgoing Messages", + description = "Would you like to Translate your Messages?", + position = 3, + group = "Player Message Translation" + ) + default boolean playerChat() + { + return false; + } + + @ConfigItem( + keyName = "playerTargetLanguage", + name = "Target Language", + description = "Language to translate messages too.", + position = 4, + group = "Player Message Translation", + hidden = true, + unhide = "playerChat" + ) + default Languages playerTargetLanguage() + { + return Languages.SPANISH; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationPlugin.java new file mode 100644 index 0000000000..85dc748859 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationPlugin.java @@ -0,0 +1,236 @@ +package net.runelite.client.plugins.chattranslation; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ObjectArrays; +import com.google.inject.Provides; +import net.runelite.api.*; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.events.PlayerMenuOptionClicked; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.input.KeyListener; +import net.runelite.client.input.KeyManager; +import net.runelite.client.menus.MenuManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginType; +import org.apache.commons.lang3.ArrayUtils; + +import javax.inject.Inject; +import javax.inject.Provider; +import java.awt.event.KeyEvent; + +@PluginDescriptor( + name = "Chat Translator", + description = "Translates messages from one Language to another.", + tags = {"translate", "language", "english", "spanish", "dutch", "french"}, + type = PluginType.UTILITY +) +public class ChatTranslationPlugin extends Plugin implements KeyListener +{ + + private static final String TRANSLATE = "Translate"; + private static final ImmutableList AFTER_OPTIONS = ImmutableList.of("Message", "Add ignore", "Remove friend", "Kick"); + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private Provider menuManager; + + @Inject + private ChatMessageManager chatMessageManager; + + @Inject + private KeyManager keyManager; + + @Inject + private ChatTranslationConfig config; + + @Provides + ChatTranslationConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(ChatTranslationConfig.class); + } + + @Override + protected void startUp() throws Exception + { + if (client != null) + { + if (config.translateOptionVisable()) + { + menuManager.get().addPlayerMenuItem(TRANSLATE); + } + } + keyManager.registerKeyListener(this); + } + + @Override + protected void shutDown() throws Exception + { + if (client != null) + { + if (config.translateOptionVisable()) + { + menuManager.get().removePlayerMenuItem(TRANSLATE); + } + } + keyManager.unregisterKeyListener(this); + } + + @Subscribe + public void onPlayerMenuOptionClicked(PlayerMenuOptionClicked event) + { + if (event.getMenuOption().equals(TRANSLATE)) + { + //TODO: Translate selected message. + } + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + if (!config.translateOptionVisable()) + { + return; + } + + int groupId = WidgetInfo.TO_GROUP(event.getActionParam1()); + String option = event.getOption(); + + if (groupId == WidgetInfo.CHATBOX.getGroupId()) + { + boolean after; + + if (!AFTER_OPTIONS.contains(option)) + { + return; + } + + final MenuEntry lookup = new MenuEntry(); + lookup.setOption(TRANSLATE); + lookup.setTarget(event.getTarget()); + lookup.setType(MenuAction.RUNELITE.getId()); + lookup.setParam0(event.getActionParam0()); + lookup.setParam1(event.getActionParam1()); + lookup.setIdentifier(event.getIdentifier()); + + MenuEntry[] newMenu = ObjectArrays.concat(lookup, client.getMenuEntries()); + int menuEntryCount = newMenu.length; + ArrayUtils.swap(newMenu, menuEntryCount - 1, menuEntryCount - 2); + client.setMenuEntries(newMenu); + } + } + + @Subscribe + public void onChatMessage(ChatMessage chatMessage) + { + if (client.getGameState() != GameState.LOADING && client.getGameState() != GameState.LOGGED_IN) + { + return; + } + switch (chatMessage.getType()) + { + case PUBLICCHAT: + case MODCHAT: + if (!config.publicChat()) + { + return; + } + break; + default: + return; + } + + String message = chatMessage.getMessage(); + + Translator translator = new Translator(); + + try + { + //Automatically check language of message and translate to selected language. + String translation = translator.translate("auto", config.publicTargetLanguage().toString(), message); + if (translation != null) + { + final MessageNode messageNode = chatMessage.getMessageNode(); + messageNode.setRuneLiteFormatMessage(translation); + chatMessageManager.update(messageNode); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + client.refreshChat(); + } + + @Override + public void keyPressed(KeyEvent event) + { + if (client.getGameState() != GameState.LOADING && client.getGameState() != GameState.LOGGED_IN) + { + return; + } + + if (!config.playerChat()) + { + return; + } + + Widget chatboxParent = client.getWidget(WidgetInfo.CHATBOX_PARENT); + + if (chatboxParent != null && chatboxParent.getOnKeyListener() != null) + { + if (event.getKeyCode() == 0xA) + { + event.consume(); + + Translator translator = new Translator(); + String message = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT); + + try + { + //Automatically check language of message and translate to selected language. + String translation = translator.translate("auto", config.playerTargetLanguage().toString(), message); + if (translation != null) + { + client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, translation); + + clientThread.invoke(() -> + { + client.runScript(96, 0, translation); + }); + } + client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, ""); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + } + + @Override + public void keyReleased(KeyEvent e) + { + // Nothing. + } + + @Override + public void keyTyped(KeyEvent e) + { + // Nothing. + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/Languages.java b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/Languages.java new file mode 100644 index 0000000000..3ec7b3ad81 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/Languages.java @@ -0,0 +1,24 @@ +package net.runelite.client.plugins.chattranslation; + +public enum Languages +{ + + ENGLISH("en"), + DUTCH("nl"), + SPANISH("es"), + FRENCH("fr"); + + private final String shortName; + + Languages(String shortName) + { + this.shortName = shortName; + } + + @Override + public String toString() + { + return shortName; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/Translator.java b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/Translator.java new file mode 100644 index 0000000000..df8f26ea29 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/Translator.java @@ -0,0 +1,46 @@ +package net.runelite.client.plugins.chattranslation; + +import org.json.JSONArray; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; + +public class Translator +{ + + public String translate(String source, String target, String message) throws Exception + { + + String url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" + source + "&tl=" + target + "&dt=t&q=" + URLEncoder.encode(message, "UTF-8"); + + URL obj = new URL(url); + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + con.setRequestProperty("User-Agent", "Mozilla/5.0"); + + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) + { + response.append(inputLine); + } + in.close(); + + return parseResult(response.toString()); + } + + private String parseResult(String inputJson) throws Exception + { + //TODO: find a way to do this using google.gson + JSONArray jsonArray = new JSONArray(inputJson); + JSONArray jsonArray2 = (JSONArray) jsonArray.get(0); + JSONArray jsonArray3 = (JSONArray) jsonArray2.get(0); + + return jsonArray3.get(0).toString(); + } + +}