@@ -44,4 +44,6 @@ public @interface ConfigItem
|
||||
boolean hidden() default false;
|
||||
|
||||
String warning() default "";
|
||||
|
||||
boolean secret() default false;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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<String, String> tags, String message)
|
||||
{
|
||||
if (client.getGameState() != GameState.LOGGED_IN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String displayName = tags.get("display-name");
|
||||
addChatMessage(displayName, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void roomstate(Map<String, String> tags)
|
||||
{
|
||||
log.debug("Room state: {}", tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void usernotice(Map<String, String> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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<String, String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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<String, String> tags, String message);
|
||||
|
||||
void roomstate(Map<String, String> tags);
|
||||
|
||||
void usernotice(Map<String, String> tags, String message);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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<String, String> 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"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user