diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java index a91e3a714e..b50094826e 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java @@ -44,4 +44,6 @@ public @interface ConfigItem boolean hidden() default false; String warning() default ""; + + boolean secret() default false; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index b78f56b1d7..34f225c044 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -56,6 +56,7 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTextArea; @@ -66,6 +67,7 @@ import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.text.JTextComponent; import lombok.extern.slf4j.Slf4j; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.Config; @@ -351,9 +353,20 @@ public class ConfigPanel extends PluginPanel if (cid.getType() == String.class) { - JTextArea textField = new JTextArea(); - textField.setLineWrap(true); - textField.setWrapStyleWord(true); + JTextComponent textField; + + if (cid.getItem().secret()) + { + textField = new JPasswordField(); + } + else + { + final JTextArea textArea = new JTextArea(); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + textField = textArea; + } + textField.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); textField.setText(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName())); @@ -548,9 +561,9 @@ public class ConfigPanel extends PluginPanel JSpinner spinner = (JSpinner) component; configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), "" + spinner.getValue()); } - else if (component instanceof JTextArea) + else if (component instanceof JTextComponent) { - JTextArea textField = (JTextArea) component; + JTextComponent textField = (JTextComponent) component; configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), textField.getText()); } else if (component instanceof JColorChooser) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchConfig.java new file mode 100644 index 0000000000..b5da132e79 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchConfig.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018, 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.twitch; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("twitch") +public interface TwitchConfig extends Config +{ + @ConfigItem( + keyName = "username", + name = "Username", + description = "Twitch Username", + position = 0 + ) + String username(); + + @ConfigItem( + keyName = "oauth", + name = "OAuth Token", + description = "Enter your OAuth token here. This can be found at http://www.twitchapps.com/tmi/", + secret = true, + position = 1 + ) + String oauthToken(); + + @ConfigItem( + keyName = "channel", + name = "Channel", + description = "Username of Twitch chat to join", + position = 2 + ) + String channel(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchPlugin.java new file mode 100644 index 0000000000..31b2977036 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchPlugin.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2018, 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.twitch; + +import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; +import java.time.temporal.ChronoUnit; +import java.util.Map; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.events.ConfigChanged; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.ChatboxInputListener; +import net.runelite.client.chat.CommandManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.events.ChatboxInput; +import net.runelite.client.events.PrivateMessageInput; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.twitch.irc.TwitchIRCClient; +import net.runelite.client.plugins.twitch.irc.TwitchListener; +import net.runelite.client.task.Schedule; + +@PluginDescriptor( + name = "Twitch", + description = "Integrates Twitch chat", + enabledByDefault = false +) +@Slf4j +public class TwitchPlugin extends Plugin implements TwitchListener, ChatboxInputListener +{ + @Inject + private TwitchConfig twitchConfig; + + @Inject + private Client client; + + @Inject + private ChatMessageManager chatMessageManager; + + @Inject + private CommandManager commandManager; + + private TwitchIRCClient twitchIRCClient; + + @Override + protected void startUp() + { + connect(); + commandManager.register(this); + } + + @Override + protected void shutDown() + { + if (twitchIRCClient != null) + { + twitchIRCClient.close(); + twitchIRCClient = null; + } + + commandManager.unregister(this); + } + + @Provides + TwitchConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(TwitchConfig.class); + } + + private void connect() + { + if (twitchConfig.username() != null + && twitchConfig.oauthToken() != null + && twitchConfig.channel() != null) + { + String channel = twitchConfig.channel(); + if (!channel.startsWith("#")) + { + channel = "#" + channel; + } + + twitchIRCClient = new TwitchIRCClient( + this, + twitchConfig.username(), + twitchConfig.oauthToken(), + channel + ); + twitchIRCClient.start(); + } + } + + @Schedule(period = 30, unit = ChronoUnit.SECONDS, asynchronous = true) + public void checkClient() + { + if (twitchIRCClient != null) + { + if (twitchIRCClient.isConnected()) + { + twitchIRCClient.pingCheck(); + } + + if (!twitchIRCClient.isConnected()) + { + log.debug("Reconnecting..."); + + connect(); + } + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged configChanged) + { + if (!configChanged.getGroup().equals("twitch")) + { + return; + } + + if (twitchIRCClient != null) + { + twitchIRCClient.close(); + twitchIRCClient = null; + } + + connect(); + } + + private void addChatMessage(String sender, String message) + { + String chatMessage = new ChatMessageBuilder() + .append(message) + .build(); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.CLANCHAT) + .sender("Twitch") + .name(sender) + .runeLiteFormattedMessage(chatMessage) + .build()); + } + + @Override + public void privmsg(Map tags, String message) + { + if (client.getGameState() != GameState.LOGGED_IN) + { + return; + } + + String displayName = tags.get("display-name"); + addChatMessage(displayName, message); + } + + @Override + public void roomstate(Map tags) + { + log.debug("Room state: {}", tags); + } + + @Override + public void usernotice(Map tags, String message) + { + log.debug("Usernotice tags: {} message: {}", tags, message); + + if (client.getGameState() != GameState.LOGGED_IN) + { + return; + } + + String sysmsg = tags.get("system-msg"); + addChatMessage("[System]", sysmsg); + } + + @Override + public boolean onChatboxInput(ChatboxInput chatboxInput) + { + String message = chatboxInput.getValue(); + if (message.startsWith("//")) + { + message = message.substring(2); + if (message.isEmpty() || twitchIRCClient == null) + { + return true; + } + + twitchIRCClient.privmsg(message); + addChatMessage(twitchConfig.username(), message); + + return true; + } + return false; + } + + @Override + public boolean onPrivateMessageInput(PrivateMessageInput privateMessageInput) + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/Message.java b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/Message.java new file mode 100644 index 0000000000..604b7a2b46 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/Message.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018, 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.twitch.irc; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +class Message +{ + private final Map tags = new HashMap<>(); + private String source; + private String command; + private String[] arguments; + + public static Message parse(String in) + { + Message message = new Message(); + if (in.startsWith("@")) + { + String[] tags = in.substring(1) + .split(";"); + for (String tag : tags) + { + int eq = tag.indexOf('='); + if (eq == -1) continue; + + String key = tag.substring(0, eq); + String value = tag.substring(eq + 1) + .replace("\\:", ";") + .replace("\\s", " ") + .replace("\\\\", "\\") + .replace("\\r", "\r") + .replace("\\n", "\n"); + + message.tags.put(key, value); + } + + int sp = in.indexOf(' '); + in = in.substring(sp + 1); + } + + if (in.startsWith(":")) + { + int sp = in.indexOf(' '); + message.source = in.substring(1, sp); + + in = in.substring(sp + 1); + } + + int sp = in.indexOf(' '); + if (sp == -1) + { + message.command = in; + message.arguments = new String[0]; + return message; + } + + message.command = in.substring(0, sp); + + String args = in.substring(sp + 1); + List argList = new ArrayList<>(); + do + { + String arg; + if (args.startsWith(":")) + { + arg = args.substring(1); + sp = -1; + } + else + { + sp = args.indexOf(' '); + arg = sp == -1 ? args : args.substring(0, sp); + } + args = args.substring(sp + 1); + argList.add(arg); + } while (sp != -1); + + message.arguments = argList.toArray(new String[0]); + return message; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/TwitchIRCClient.java b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/TwitchIRCClient.java new file mode 100644 index 0000000000..f76ddcaa3c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/TwitchIRCClient.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2018, 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.twitch.irc; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class TwitchIRCClient extends Thread implements AutoCloseable +{ + private static final String HOST = "irc.chat.twitch.tv"; + private static final int PORT = 6697; + private static final int READ_TIMEOUT = 60000; // ms + private static final int PING_TIMEOUT = 30000; // ms + + private final TwitchListener twitchListener; + + private final String username, password; + private final String channel; + + private Socket socket; + private BufferedReader in; + private PrintWriter out; + private long last; + private boolean pingSent; + + public TwitchIRCClient(TwitchListener twitchListener, String username, String password, String channel) + { + setName("Twitch"); + this.twitchListener = twitchListener; + this.username = username; + this.password = password; + this.channel = channel; + } + + @Override + public void close() + { + try + { + socket.close(); + } + catch (IOException ex) + { + log.warn("error closing socket", ex); + } + + in = null; + out = null; + } + + @Override + public void run() + { + try + { + SocketFactory socketFactory = SSLSocketFactory.getDefault(); + socket = socketFactory.createSocket(HOST, PORT); + socket.setSoTimeout(READ_TIMEOUT); + + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + out = new PrintWriter(socket.getOutputStream()); + } + catch (IOException ex) + { + log.warn("unable to setup irc client", ex); + return; + } + + register(username, password); + join(channel); + + try + { + String line; + + while ((line = read()) != null) + { + log.debug("<- {}", line); + + last = System.currentTimeMillis(); + pingSent = false; + + Message message = Message.parse(line); + + switch (message.getCommand()) + { + case "PING": + send("PONG", message.getArguments()[0]); + break; + case "PRIVMSG": + twitchListener.privmsg(message.getTags(), + message.getArguments()[1]); + break; + case "ROOMSTATE": + twitchListener.roomstate(message.getTags()); + break; + case "USERNOTICE": + twitchListener.usernotice(message.getTags(), + message.getArguments().length > 0 ? message.getArguments()[0] : null); + break; + } + } + } + catch (IOException ex) + { + log.debug("error in twitch irc client", ex); + } + finally + { + try + { + socket.close(); + } + catch (IOException e) + { + log.warn(null, e); + } + } + } + + public boolean isConnected() + { + return socket != null && socket.isConnected() && !socket.isClosed(); + } + + public void pingCheck() + { + if (out == null) + { + // client is not connected yet + return; + } + + if (!pingSent && System.currentTimeMillis() - last >= PING_TIMEOUT) + { + ping("twitch"); + pingSent = true; + } + else if (pingSent) + { + log.debug("Ping timeout, disconnecting."); + close(); + } + } + + private void register(String username, String oauth) + { + send("CAP", "REQ", "twitch.tv/commands twitch.tv/tags"); + send("PASS", oauth); + send("NICK", username); + } + + private void join(String channel) + { + send("JOIN", channel); + } + + private void ping(String destination) + { + send("PING", destination); + } + + public void privmsg(String message) + { + send("PRIVMSG", channel, message); + } + + private void send(String command, String... args) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(command); + for (int i = 0; i < args.length; ++i) + { + stringBuilder.append(' '); + if (i + 1 == args.length) + { + stringBuilder.append(':'); + } + stringBuilder.append(args[i]); + } + + log.debug("-> {}", stringBuilder.toString()); + + stringBuilder.append("\r\n"); + + out.write(stringBuilder.toString()); + out.flush(); + } + + private String read() throws IOException + { + String line = in.readLine(); + if (line == null) + { + return null; + } + int len = line.length(); + while (len > 0 && (line.charAt(len - 1) == '\r' || line.charAt(len - 1) == '\n')) + { + --len; + } + + return line.substring(0, len); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/TwitchListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/TwitchListener.java new file mode 100644 index 0000000000..77dec8d108 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/irc/TwitchListener.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, 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.twitch.irc; + +import java.util.Map; + +public interface TwitchListener +{ + void privmsg(Map tags, String message); + + void roomstate(Map tags); + + void usernotice(Map tags, String message); +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/twitch/irc/MessageTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/twitch/irc/MessageTest.java new file mode 100644 index 0000000000..7ef1b4d839 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/twitch/irc/MessageTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018, 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.twitch.irc; + +import java.util.Map; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class MessageTest +{ + @Test + public void testParse() + { + Message message = Message.parse("@badges=subscriber/0;color=;display-name=kappa_kid_;emotes=;id=6539b42a-e945-4a83-a5b7-018149ca9fa7;mod=0;room-id=27107346;subscriber=1;tmi-sent-ts=1535926830652;turbo=0;user-id=33390095;user-type= :kappa_kid_!kappa_kid_@kappa_kid_.tmi.twitch.tv PRIVMSG #b0aty :how do u add charges to that zeah book?"); + Map messageTags = message.getTags(); + assertEquals("subscriber/0", messageTags.get("badges")); + assertEquals("kappa_kid_!kappa_kid_@kappa_kid_.tmi.twitch.tv", message.getSource()); + assertEquals("PRIVMSG", message.getCommand()); + assertEquals("#b0aty", message.getArguments()[0]); + assertEquals("how do u add charges to that zeah book?", message.getArguments()[1]); + + message = Message.parse("@badges=moderator/1,subscriber/12,bits/10000;color=#008000;display-name=Am_Sephiroth;emotes=;id=7d516b7c-de7a-4c8b-ad23-d8880b55d46b;login=am_sephiroth;mod=1;msg-id=subgift;msg-param-months=8;msg-param-recipient-display-name=IntRS;msg-param-recipient-id=189672346;msg-param-recipient-user-name=intrs;msg-param-sender-count=215;msg-param-sub-plan-name=Sick\\sNerd\\sSubscription\\s;msg-param-sub-plan=1000;room-id=49408183;subscriber=1;system-msg=Am_Sephiroth\\sgifted\\sa\\sTier\\s1\\ssub\\sto\\sIntRS!\\sThey\\shave\\sgiven\\s215\\sGift\\sSubs\\sin\\sthe\\schannel!;tmi-sent-ts=1535980032939;turbo=0;user-id=69539403;user-type=mod :tmi.twitch.tv USERNOTICE #sick_nerd"); + messageTags = message.getTags(); + assertEquals("Am_Sephiroth gifted a Tier 1 sub to IntRS! They have given 215 Gift Subs in the channel!", messageTags.get("system-msg")); + } +} \ No newline at end of file