diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java index 988e73a625..21eb1eab10 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java @@ -26,6 +26,8 @@ package net.runelite.client.plugins.emojis; import java.awt.image.BufferedImage; import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.annotation.Nullable; import javax.inject.Inject; import joptsimple.internal.Strings; @@ -52,6 +54,8 @@ import net.runelite.client.util.ImageUtil; @Slf4j public class EmojiPlugin extends Plugin { + private static final Pattern TAG_REGEXP = Pattern.compile("<[^>]*>"); + @Inject private Client client; @@ -128,7 +132,8 @@ public class EmojiPlugin extends Plugin return; } - final String message = chatMessage.getMessage(); + final MessageNode messageNode = chatMessage.getMessageNode(); + final String message = messageNode.getValue(); final String updatedMessage = updateMessage(message); if (updatedMessage == null) @@ -136,7 +141,6 @@ public class EmojiPlugin extends Plugin return; } - final MessageNode messageNode = chatMessage.getMessageNode(); messageNode.setRuneLiteFormatMessage(updatedMessage); chatMessageManager.update(messageNode); client.refreshChat(); @@ -169,7 +173,9 @@ public class EmojiPlugin extends Plugin boolean editedMessage = false; for (int i = 0; i < messageWords.length; i++) { - final Emoji emoji = Emoji.getEmoji(messageWords[i]); + // Remove tags except for and + final String trigger = removeTags(messageWords[i]); + final Emoji emoji = Emoji.getEmoji(trigger); if (emoji == null) { @@ -178,7 +184,7 @@ public class EmojiPlugin extends Plugin final int emojiId = modIconsStart + emoji.ordinal(); - messageWords[i] = ""; + messageWords[i] = messageWords[i].replace(trigger, ""); editedMessage = true; } @@ -190,4 +196,29 @@ public class EmojiPlugin extends Plugin return Strings.join(messageWords, " "); } + + /** + * Remove tags, except for <lt> and <gt> + * + * @return + */ + private static String removeTags(String str) + { + StringBuffer stringBuffer = new StringBuffer(); + Matcher matcher = TAG_REGEXP.matcher(str); + while (matcher.find()) + { + matcher.appendReplacement(stringBuffer, ""); + String match = matcher.group(0); + switch (match) + { + case "": + case "": + stringBuffer.append(match); + break; + } + } + matcher.appendTail(stringBuffer); + return stringBuffer.toString(); + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java new file mode 100644 index 0000000000..3427246946 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.emojis; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.IndexedSprite; +import net.runelite.api.MessageNode; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameStateChanged; +import net.runelite.client.chat.ChatMessageManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class EmojiPluginTest +{ + @Mock + @Bind + private Client client; + + @Mock + @Bind + private ChatMessageManager chatMessageManager; + + @Inject + private EmojiPlugin emojiPlugin; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testOnChatMessage() + { + when(client.getGameState()).thenReturn(GameState.LOGGED_IN); + when(client.getModIcons()).thenReturn(new IndexedSprite[0]); + when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class)); + + // Trip emoji loading + GameStateChanged gameStateChanged = new GameStateChanged(); + gameStateChanged.setGameState(GameState.LOGGED_IN); + emojiPlugin.onGameStateChanged(gameStateChanged); + + MessageNode messageNode = mock(MessageNode.class); + // With chat recolor, message may be wrapped in col tags + when(messageNode.getValue()).thenReturn(":) :) :)"); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setMessageNode(messageNode); + + emojiPlugin.onChatMessage(chatMessage); + + verify(messageNode).setRuneLiteFormatMessage(" "); + } + + @Test + public void testGtLt() + { + when(client.getGameState()).thenReturn(GameState.LOGGED_IN); + when(client.getModIcons()).thenReturn(new IndexedSprite[0]); + when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class)); + + // Trip emoji loading + GameStateChanged gameStateChanged = new GameStateChanged(); + gameStateChanged.setGameState(GameState.LOGGED_IN); + emojiPlugin.onGameStateChanged(gameStateChanged); + + MessageNode messageNode = mock(MessageNode.class); + when(messageNode.getValue()).thenReturn(":D"); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setMessageNode(messageNode); + + emojiPlugin.onChatMessage(chatMessage); + + verify(messageNode).setRuneLiteFormatMessage(""); + } +} \ No newline at end of file