Commit 5edfbf50 authored by Adam Gausmann's avatar Adam Gausmann

Implement basic support for multiple connections (WIP)

Automatic connections are not reimplemented yet.
parent 8387e88e
Pipeline #5008417 passed with stages
in 23 seconds
package ninja.nonemu.samurai.connection;
import java.io.IOException;
import ninja.nonemu.irc.ChatRecipient;
public interface Channel extends ChatRecipient {
String getName();
ninja.nonemu.irc.Channel moreInfo() throws IOException;
Connection getConnection();
}
package ninja.nonemu.samurai.connection;
import java.io.IOException;
import ninja.nonemu.irc.Message;
public interface Connection {
/**
* Sends a custom message to the server.
* @param message The message to send.
*/
void sendMessage(Message message);
/**
* Checks whether a connection exists by checking if there is a socket and it says it is connected.
* @return true if there is a connection; false otherwise.
*/
boolean isConnected();
/**
* Connects to the network using the details provided during construction.
*
* <br>This may send any/all of the following: PASS, NICK, and USER messages.
*
* @throws IOException if an IO error occurs while connecting.
*/
void connect() throws IOException;
/**
* Disconnects from any connected network.
*
* @param comment The comment to leave when disconnecting.
* @throws IOException if an IO error occurs while disconnecting.
*/
void disconnect(String comment) throws IOException;
/**
* Disconnects from any connected network without providing a comment.
*
* @throws IOException if an IO error occurs while disconnecting.
*/
void disconnect() throws IOException;
/**
* Attempts to set a new nickname for the client.
*
* @param nickname The new nickname.
*/
void setNickname(String nickname);
/**
* Attempts to join a channel.
*
* @param channel The channel to join.
*/
void joinChannel(String channel);
/**
* Attempts to leave a channel, leaving a comment behind.
* @param channel The channel to leave.
* @param comment The comment/reason for leaving.
*/
void leaveChannel(String channel, String comment);
/**
* Attempts to leave a channel with no comment.
* @param channel The channel to leave.
*/
void leaveChannel(String channel);
/**
* Sends a chat message to a channel or user connected to the network.
* @param target The entity receiving the message.
* @param message The message to send.
*/
void sendChat(String target, String message);
Channel getChannel(String channel);
User getUser(String userId);
User getUser(UserMask userMask);
/**
* Gets the currently-configured host that the client is connecting to.
*/
String getHost();
/**
* Gets the currently-configured port that the client is connecting to.
*/
int getPort();
/**
* Gets the currently-configured nickname of the client.
*/
String getNickname();
}
package ninja.nonemu.samurai.connection;
import java.io.IOException;
import ninja.nonemu.irc.Message;
import java.util.List;
import ninja.nonemu.irc.ConnectionBuilder;
/**
* Manages the connection to the IRC network and is responsible for processing messages sent and received.
* Manages connections to IRC networks.
* Since multiple simultaneous connections are allowed, this class is provided to allow plugins to add and remove
* connections on their own.
*
* @see Connection
*/
public interface ConnectionManager {
List<Connection> forHost(String hostname);
/**
* Sends a custom message to the server.
* @param message The message to send.
*/
void sendMessage(Message message);
Connection[] getConnections();
/**
* Checks whether a connection exists by checking if there is a socket and it says it is connected.
* @return true if there is a connection; false otherwise.
*/
boolean isConnected();
void addConnection(ConnectionBuilder builder);
/**
* Connects to the network using the details provided during construction.
*
* <br>This may send any/all of the following: PASS, NICK, and USER messages.
*
* @throws IOException if an IO error occurs while connecting.
*/
void connect() throws IOException;
/**
* Disconnects from any connected network.
*
* @param comment The comment to leave when disconnecting.
* @throws IOException if an IO error occurs while disconnecting.
*/
void disconnect(String comment) throws IOException;
/**
* Disconnects from any connected network without providing a comment.
*
* @throws IOException if an IO error occurs while disconnecting.
*/
void disconnect() throws IOException;
/**
* Attempts to set a new nickname for the client.
*
* @param nickname The new nickname.
*/
void setNickname(String nickname);
/**
* Attempts to join a channel.
*
* @param channel The channel to join.
*/
void joinChannel(String channel);
/**
* Attempts to leave a channel, leaving a comment behind.
* @param channel The channel to leave.
* @param comment The comment/reason for leaving.
*/
void leaveChannel(String channel, String comment);
/**
* Attempts to leave a channel with no comment.
* @param channel The channel to leave.
*/
void leaveChannel(String channel);
/**
* Sends a chat message to a channel or user connected to the network.
* @param target The entity receiving the message.
* @param message The message to send.
*/
void sendChat(String target, String message);
Channel getChannel(String channel);
User getUser(String userId);
User getUser(UserMask userMask);
/**
* Gets the currently-configured host that the client is connecting to.
*/
String getHost();
/**
* Gets the currently-configured port that the client is connecting to.
*/
int getPort();
/**
* Gets the currently-configured nickname of the client.
*/
String getNickname();
void removeConnection(Connection connection);
}
......@@ -17,4 +17,6 @@ public interface User extends ChatSender, ChatRecipient, CommandSender {
String getUsername();
String getHostname();
Connection getConnection();
}
......@@ -5,6 +5,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import ninja.nonemu.samurai.BotImpl;
import ninja.nonemu.samurai.connection.Connection;
import ninja.nonemu.samurai.connection.User;
import ninja.nonemu.samurai.connection.UserMask;
public class CommandExecutorImpl {
......@@ -191,36 +193,56 @@ public class CommandExecutorImpl {
}
private boolean handleQuit(CommandSender sender, CommandInfo info, String label, String[] args) throws IOException {
sender.sendChat("Goodbye!");
if (args.length > 0) {
bot.getConnectionManager().disconnect(String.join(" ", (CharSequence[]) args));
if (args.length == 0) {
if (!(sender instanceof User)) {
sender.sendChat("Console must provide a connection name.");
return true;
}
((User) sender).getConnection().disconnect();
} else {
bot.getConnectionManager().disconnect();
for (Connection connection : bot.getConnectionManager().forHost(args[0])) {
connection.disconnect();
}
}
sender.sendChat("Goodbye!");
return true;
}
private boolean handleJoin(CommandSender sender, CommandInfo info, String label, String[] args) {
if (args.length > 0) {
if (args.length == 1) {
if (!(sender instanceof User)) {
sender.sendChat("Console must provide a connection name.");
return true;
}
((User) sender).getConnection().joinChannel(args[0]);
} else {
for (Connection connection : bot.getConnectionManager().forHost(args[1])) {
connection.joinChannel(args[0]);
}
}
sender.sendChat("Joining channel " + args[0]);
bot.getConnectionManager().joinChannel(args[0]);
return true;
}
return false;
}
private boolean handleLeave(CommandSender sender, CommandInfo info, String label, String[] args) {
if (args.length > 1) {
sender.sendChat("Leaving channel " + args[0]);
String[] newArgs = new String[args.length - 1];
System.arraycopy(args, 1, newArgs, 0, newArgs.length);
bot.getConnectionManager().leaveChannel(args[0], String.join(" ", (CharSequence[]) newArgs));
return true;
}
if (args.length > 0) {
if (args.length == 1) {
if (!(sender instanceof User)) {
sender.sendChat("Console must provide a connection name.");
return true;
}
((User) sender).getConnection().leaveChannel(args[0]);
} else {
for (Connection connection : bot.getConnectionManager().forHost(args[1])) {
connection.leaveChannel(args[0]);
}
}
sender.sendChat("Leaving channel " + args[0]);
bot.getConnectionManager().leaveChannel(args[0]);
return true;
}
return false;
......@@ -228,10 +250,15 @@ public class CommandExecutorImpl {
private boolean handleMsg(CommandSender sender, CommandInfo info, String label, String[] args) {
if (args.length > 1) {
if (!(sender instanceof User)) {
sender.sendChat("Console may not use this command.");
return true;
}
String[] words = new String[args.length - 1];
System.arraycopy(args, 1, words, 0, words.length);
bot.getConnectionManager().sendChat(args[0], String.join(" ", (CharSequence[]) words));
((User) sender).getConnection().sendChat(args[0], String.join(" ", (CharSequence[]) words));
return true;
}
......
package ninja.nonemu.samurai.connection;
import java.io.IOException;
import ninja.nonemu.samurai.BotImpl;
public class ChannelImpl implements Channel {
private final BotImpl bot;
private final ConnectionImpl connection;
private final String name;
public ChannelImpl(BotImpl bot, String name) {
this.bot = bot;
public ChannelImpl(ConnectionImpl connection, String name) {
this.connection = connection;
this.name = name;
}
......@@ -19,13 +16,13 @@ public class ChannelImpl implements Channel {
}
@Override
public ninja.nonemu.irc.Channel moreInfo() throws IOException {
return bot.getConnectionManager().getConnection().getChannel(name);
public Connection getConnection() {
return connection;
}
@Override
public void sendChat(String message) {
bot.getConnectionManager().sendChat(name, message);
connection.sendChat(name, message);
}
@Override
......@@ -35,13 +32,14 @@ public class ChannelImpl implements Channel {
Channel channel = (Channel) o;
return name.equals(channel.getName());
return connection.equals(channel.getConnection())
&& name.equalsIgnoreCase(channel.getName());
}
@Override
public int hashCode() {
int result = bot.hashCode();
result = 31 * result + name.hashCode();
int result = connection.hashCode();
result = 31 * result + name.toLowerCase().hashCode();
return result;
}
......
package ninja.nonemu.samurai.connection;
import java.io.IOException;
import ninja.nonemu.irc.ChatRecipient;
import ninja.nonemu.irc.IrcUtil;
import ninja.nonemu.irc.Message;
import ninja.nonemu.samurai.BotImpl;
import org.apache.logging.log4j.Logger;
public class ConnectionImpl implements Connection {
private final ninja.nonemu.irc.Connection connection;
private final BotImpl bot;
private final Logger logger;
public ConnectionImpl(BotImpl bot, ninja.nonemu.irc.Connection connection) {
this.connection = connection;
this.bot = bot;
logger = bot.getLogger();
}
public void run() {
try {
while (connection.canReadMessage()) {
Message message = readMessage();
if (message.getCommand().equalsIgnoreCase("PRIVMSG")) {
String sender = message.senderName();
String target = message.getArg(0);
String text = message.getArg(1);
bot.getEventSystem().dispatchEvent(new ChatEvent(getUser(sender),
recipientByName(target), text));
}
else if (message.getCommand().equalsIgnoreCase("NOTICE")) {
String sender = message.senderName();
String target = message.getArg(0);
String text = message.getArg(1);
bot.getEventSystem().dispatchEvent(new NoticeEvent(getUser(sender),
recipientByName(target), text));
}
else if (message.getCommand().equalsIgnoreCase("JOIN")) {
String user = message.senderName();
String channel = message.getArg(0);
if (user.equalsIgnoreCase(getNickname())) {
bot.getEventSystem().dispatchEvent(new BotJoinEvent(getChannel(channel)));
} else {
bot.getEventSystem().dispatchEvent(new UserJoinEvent(getUser(user),
getChannel(channel)));
}
}
else if (message.getCommand().equalsIgnoreCase("PART")) {
String user = message.senderName();
String channel = message.getArg(0);
String comment = message.getArg(1);
if (user.equalsIgnoreCase(getNickname())) {
bot.getEventSystem().dispatchEvent(new BotLeaveEvent(getChannel(channel), comment));
} else {
bot.getEventSystem().dispatchEvent(new UserLeaveEvent(getUser(user),
getChannel(channel), comment));
}
}
else if (message.getCommand().equalsIgnoreCase("KICK")) {
String sender = message.senderName();
String channel = message.getArg(0);
String user = message.getArg(1);
String comment = message.getArg(2);
if (user.equals(getNickname())) {
bot.getEventSystem().dispatchEvent(new BotKickEvent(getUser(sender), getChannel(channel),
comment));
} else {
bot.getEventSystem().dispatchEvent(new UserKickEvent(getUser(sender),
getUser(user), getChannel(channel), comment));
}
}
else if (message.getCommand().equalsIgnoreCase("QUIT")) {
String user = message.senderName();
String comment = message.getArg(0);
bot.getEventSystem().dispatchEvent(new UserQuitEvent(getUser(user), comment));
}
else if (message.getCommand().equalsIgnoreCase("KILL")) {
String sender = message.senderName();
String user = message.getArg(0);
String comment = message.getArg(1);
bot.getEventSystem().dispatchEvent(new UserKillEvent(getUser(sender),
getUser(user), comment));
}
else if (message.getCommand().equalsIgnoreCase("ERROR")) {
logger.info("Server error - {}", message.getArg(0));
if (isConnected()) {
disconnect();
break;
}
}
else if (message.getCommand().equals("001")) { // RPL_WELCOME
bot.getEventSystem().dispatchEvent(new BotConnectEvent(getHost(), getPort(), getNickname()));
String channels = bot.getProperty("channels");
if (channels != null && !channels.isEmpty()) {
connection.joinChannel(channels);
}
}
else if (message.getCommand().equals("432")) { // ERR_ERRONEUSNICKNAME
logger.error("Bad nickname (consider changing it): {}", message.getArg(1));
}
else if (message.getCommand().equals("433")) { // ERR_NICKNAMEINUSE
logger.warn("Nickname is in use; trying an alternate name");
setNickname(message.getArg(1) + "_");
}
}
} catch (IOException e) {
logger.error("Error reading messages from server", e);
}
}
private Message readMessage() throws IOException {
return connection.readMessage();
}
private ChatRecipient recipientByName(String name) {
if (IrcUtil.isChannel(name)) {
return getChannel(name);
}
return getUser(name);
}
public ninja.nonemu.irc.Connection getInnerConnection() {
return connection;
}
@Override
public void sendMessage(Message message) {
connection.sendMessage(message);
}
@Override
public boolean isConnected() {
return connection.isConnected();
}
@Override
public void connect() throws IOException {
connection.connect();
}
@Override
public void disconnect(String comment) throws IOException {
connection.disconnect(comment);
}
@Override
public void disconnect() throws IOException {
connection.disconnect();
}
@Override
public void setNickname(String nickname) {
connection.setNickname(nickname);
}
@Override
public void joinChannel(String channel) {
connection.joinChannel(channel);
}
@Override
public void leaveChannel(String channel, String comment) {
connection.leaveChannel(channel, comment);
}
@Override
public void leaveChannel(String channel) {
connection.leaveChannel(channel);
}
@Override
public void sendChat(String target, String message) {
connection.sendChat(target, message);
}
@Override
public Channel getChannel(String channel) {
return new ChannelImpl(this, channel);
}
@Override
public User getUser(String userId) {
return new UserImpl(bot, this, userId);
}
@Override
public User getUser(UserMask userMask) {
return new UserImpl(bot, this, userMask);
}
@Override
public String getHost() {
return connection.getHost();
}
@Override
public int getPort() {
return connection.getPort();
}
@Override
public String getNickname() {
return connection.getNickname();
}
}
......@@ -5,18 +5,20 @@ import ninja.nonemu.samurai.BotImpl;
public class UserImpl implements User {
private final BotImpl bot;
private final ConnectionImpl connection;
private final UserMask mask;
public UserImpl(BotImpl bot, UserMask mask) {
public UserImpl(BotImpl bot, ConnectionImpl connection, UserMask mask) {
this.bot = bot;
this.connection = connection;
this.mask = mask;
if (mask.isWildcard()) {
throw new IllegalArgumentException("Wildcard masks can't be used for users.");
}
}
public UserImpl(BotImpl bot, String mask) {
this(bot, new UserMask(mask));
public UserImpl(BotImpl bot, ConnectionImpl connection, String mask) {
this(bot, connection, new UserMask(mask));
}
/**
......@@ -69,9 +71,14 @@ public class UserImpl implements User {
return mask.getHostname();
}
@Override
public Connection getConnection() {
return connection;
}
@Override
public void sendChat(String message) {
bot.getConnectionManager().sendChat(getNickname(), message);
connection.sendChat(getNickname(), message);
}
@Override
......@@ -81,7 +88,8 @@ public class UserImpl implements User {
User user = (User) o;
return mask.equals(user.getMask());
return mask.equals(user.getMask()) &&
connection.equals(user.getConnection());
}
@Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment