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..32e8481c7c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationConfig.java @@ -0,0 +1,91 @@ +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 = "translateOptionVisable", + name = "Show 'Translate' menu option", + description = "Adds 'Translate' to the right-click menu in the Chatbox.", + position = 0, + group = "Public Chat Translation" + ) + default boolean translateOptionVisable() + { + return true; + } + + @ConfigItem( + keyName = "publicChat", + name = "Translate incoming Messages", + description = "Would you like to Translate Public Chat?", + position = 1, + group = "Public Chat Translation", + hidden = true, + unhide = "translateOptionVisable" + ) + default boolean publicChat() + { + return false; + } + + @ConfigItem( + keyName = "playerNames", + name = "Translated Player list:", + description = "Players you add to this list will be Translated in Public chat.", + position = 2, + group = "Public Chat Translation", + hidden = true, + unhide = "translateOptionVisable" + ) + default String getPlayerNames() + { + return ""; + } + + @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..65668483e2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chattranslation/ChatTranslationPlugin.java @@ -0,0 +1,280 @@ +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.ConfigChanged; +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 net.runelite.client.util.Text; +import org.apache.commons.lang3.ArrayUtils; + +import javax.inject.Inject; +import javax.inject.Provider; +import java.awt.event.KeyEvent; +import java.util.ArrayList; + +@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"); + + private ArrayList playerNames = new ArrayList<>(); + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private ConfigManager configManager; + + @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); + + playerNames.addAll(Text.fromCSV(config.getPlayerNames())); + } + + @Override + protected void shutDown() throws Exception + { + if (client != null) + { + if (config.translateOptionVisable()) + { + menuManager.get().removePlayerMenuItem(TRANSLATE); + } + } + keyManager.unregisterKeyListener(this); + + playerNames.clear(); + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals("chattranslation")) + { + if (event.getKey().equals("playerNames")) + { + for (String names : Text.fromCSV(config.getPlayerNames())) + { + if (!playerNames.contains(Text.toJagexName(names))) + { + playerNames.add(Text.toJagexName(names)); + } + } + } + } + } + + @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 menuEntry = new MenuEntry(); + menuEntry.setOption(TRANSLATE); + menuEntry.setTarget(event.getTarget()); + menuEntry.setType(MenuAction.RUNELITE.getId()); + menuEntry.setParam0(event.getActionParam0()); + menuEntry.setParam1(event.getActionParam1()); + menuEntry.setIdentifier(event.getIdentifier()); + + MenuEntry[] newMenu = ObjectArrays.concat(menuEntry, client.getMenuEntries()); + int menuEntryCount = newMenu.length; + ArrayUtils.swap(newMenu, menuEntryCount - 1, menuEntryCount - 2); + client.setMenuEntries(newMenu); + } + } + + @Subscribe + public void onPlayerMenuOptionClicked(PlayerMenuOptionClicked event) + { + if (event.getMenuOption().equals(TRANSLATE)) + { + String name = Text.toJagexName(event.getMenuTarget()); + if (!playerNames.contains(name)) + { + playerNames.add(name); + } + + configManager.setConfiguration("chattranslation", "playerNames", Text.toCSV(playerNames)); + configManager.sendConfig(); + } + } + + @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; + } + + for (String nameList : playerNames) + { + if (nameList.contains(Text.toJagexName(chatMessage.getName()))) + { + 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(); + } + +}