package lib.pircbot;
import gui.forms.GUIMain;
import util.Utils;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
/**
* Created by Nick on 9/7/2015.
*/
public class PircBotConnection {
private InputThread _inputThread = null;
private OutputThread _outputThread = null;
private ArrayBlockingQueue<String> _outQueue;
private PircBot bot;
private ConnectionType type;
private String _server, name;
public OutputThread getOutputThread() {
return _outputThread;
}
public ArrayBlockingQueue<String> getOutQueue()
{
return _outQueue;
}
public PircBot getBot() {
return bot;
}
public void setName(String name)
{
this.name = name;
}
public String getName() {
return name;
}
public ConnectionType getType() {
return type;
}
private InetAddress _inetAddress = null;
public enum ConnectionType {
NORMAL(6667, "irc.chat.twitch.tv");
int port;
String[] hosts;
ConnectionType(int port, String... hosts) {
this.port = port;
this.hosts = hosts;
}
}
public PircBotConnection(PircBot bot, ConnectionType type) {
this.bot = bot;
this.type = type;
this.name = bot.getNick();
this._outQueue = new ArrayBlockingQueue<>(500, true); // I don't know if people will use more than 500 msgs
}
/**
* Returns whether or not the PircBot is currently connected to a server.
* The result of this method should only act as a rough guide,
* as the result may not be valid by the time you act upon it.
*
* @return True if and only if the PircBot is currently connected to a server.
*/
public boolean isConnected() {
return _inputThread != null && _inputThread.isConnected();
}
public boolean connect() {
if (isConnected()) {
return false;
}
_server = type.hosts[Utils.random(0, type.hosts.length)];
// Connect to the server.
Socket socket;
InputStream socketIn;
OutputStream socketOut;
try {
socket = new Socket(_server, type.port);
bot.log("*** Trying to connect to " + _server + " on port " + getPort());
socketIn = socket.getInputStream();
socketOut = socket.getOutputStream();
} catch (Exception e) {
GUIMain.log(e);
return false;
}
bot.log("*** Connected to server.");
_inetAddress = socket.getLocalAddress();
InputStreamReader inputStreamReader;
OutputStreamWriter outputStreamWriter;
// Assume the specified encoding is valid for this JVM.
try {
inputStreamReader = new InputStreamReader(socketIn, "UTF-8");
outputStreamWriter = new OutputStreamWriter(socketOut, "UTF-8");
} catch (Exception e) {
return false;
}
BufferedReader breader = new BufferedReader(inputStreamReader);
BufferedWriter bwriter = new BufferedWriter(outputStreamWriter);
_outputThread = new OutputThread(bot, _outQueue, bwriter);
// Attempt to join the server.
_outputThread.sendRawLine("PASS " + bot.getPassword());
_outputThread.sendRawLine("NICK " + bot.getNick());
_inputThread = new InputThread(this, socket, breader);
// Read stuff back from the server to see if we connected.
String line;
try {
while ((line = breader.readLine()) != null) {
bot.log(line);
bot.handleLine(line);
if (line.contains("Login unsuccessful")) {
socket.close();
_inputThread.dispose();
_inputThread = null;
return false;
}
int firstSpace = line.indexOf(" ");
int secondSpace = line.indexOf(" ", firstSpace + 1);
if (secondSpace >= 0) {
String code = line.substring(firstSpace + 1, secondSpace);
if (code.equals("004")) {
// We're connected to the server.
break;
} else if (code.startsWith("5") || code.startsWith("4")) {
socket.close();
_inputThread = null;
return false;
}
}
}
} catch (Exception e) {
try {
socket.close();
} catch (Exception ignored) {
}
_inputThread = null;
return false;
}
bot.log("*** Logged onto server.");
// This makes the socket timeout on read operations after 5 minutes.
// Maybe in some future version I will let the user change this at runtime.
try {
socket.setSoTimeout(5 * 60 * 1000);
} catch (Exception e) {
return false;
}
// Now start the InputThread to read all other lines from the server.
_inputThread.start();
// Now start the outputThread that will be used to send all messages.
_outputThread.start();
getOutputThread().sendRawLine("CAP REQ :twitch.tv/tags");
getOutputThread().sendRawLine("CAP REQ :twitch.tv/commands");
return true;
}
/**
* Disposes of all thread resources used by this PircBot. This may be
* useful when writing bots or clients that use multiple servers (and
* therefore multiple PircBot instances) or when integrating a PircBot
* with an existing program.
* <p>
* Each PircBot runs its own threads for dispatching messages from its
* outgoing message queue and receiving messages from the server.
* Calling dispose() ensures that these threads are
* stopped, thus freeing up system resources and allowing the PircBot
* object to be garbage collected if there are no other references to
* it.
* <p>
* Once a PircBot object has been disposed, it should not be used again.
* Attempting to use a PircBot that has been disposed may result in
* unpredictable behaviour.
*
* @since 1.2.2
*/
public void dispose() {
if (_outputThread != null) _outputThread.interrupt();
if (_inputThread != null) _inputThread.dispose();
}
/**
* Returns the name of the last IRC server the PircBot tried to connect to.
* This does not imply that the connection attempt to the server was
* successful (we suggest you look at the onConnect method).
* A value of null is returned if the PircBot has never tried to connect
* to a server.
*
* @return The name of the last machine we tried to connect to. Returns
* null if no connection attempts have ever been made.
*/
public final String getServer() {
return _server;
}
/**
* Returns the port number of the last IRC server that the PircBot tried
* to connect to.
* This does not imply that the connection attempt to the server was
* successful (we suggest you look at the onConnect method).
* A value of -1 is returned if the PircBot has never tried to connect
* to a server.
*
* @return The port number of the last IRC server we connected to.
* Returns -1 if no connection attempts have ever been made.
* @since PircBot 0.9.9
*/
public final int getPort() {
return type.port;
}
/**
* Returns the InetAddress used by the PircBot.
* This can be used to find the I.P. address from which the PircBot is
* connected to a server.
*
* @return The current local InetAddress, or null if never connected.
* @since PircBot 1.4.4
*/
public InetAddress getInetAddress() {
return _inetAddress;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof PircBotConnection) &&
(((PircBotConnection) obj).getType() == this.getType()) &&
((PircBotConnection) obj).getName().equals(this.getName()) &&
((PircBotConnection) obj).getBot().equals(this.getBot());
}
public void sendRawLine(String line) {
if (getOutputThread() == null || line == null) return;
getOutputThread().sendRawLine(line);
}
}