Commit 2183f10b authored by Adam Gausmann's avatar Adam Gausmann

Merge branch 'autoconnect' into 'master'

Add connection builder and autoconnect options

See merge request !16
parents f507f11f 64f64f3e
Pipeline #5322081 passed with stages
in 17 seconds
......@@ -15,7 +15,7 @@
<dependency>
<groupId>ninja.nonemu</groupId>
<artifactId>ircninja</artifactId>
<version>1.1</version>
<version>1.1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
......
......@@ -5,7 +5,11 @@ import ninja.nonemu.irc.Message;
public interface Connection {
String getName();
/**
* Gets the identifier string of this connection as set by {@link ConnectionBuilder#id(String)}.
* @return The ID string (case insensitive).
*/
String getId();
/**
* Sends a custom message to the server.
......
package ninja.nonemu.samurai.connection;
/**
* The class which builds new {@link Connection} instances.
* An implementation is constructed and returned when you call {@link ConnectionManager#addConnection()}. That
* implementation will automatically add the connection when you call {@link #build()}.
*
* @see ConnectionManager
*/
public interface ConnectionBuilder {
/**
* Sets the identifying string of the connection.
* This is used by methods like {@link ConnectionManager#getConnection(String)} and
* {@link ConnectionManager#removeConnection(String)}. It will also be returned by {@link Connection#getId()}.
* If no ID is specified, the hostname is used as the ID.
*
* @param id The new connection's ID (case insensitive).
*/
ConnectionBuilder id(String id);
/**
* Sets the hostname of the server to connect to. This is required; no default is available.
*
* @param host The hostname (case insensitive).
*/
ConnectionBuilder host(String host);
/**
* Sets the port of the server to connect to (default 6667).
*
* @param port The port.
*/
ConnectionBuilder port(int port);
/**
* Sets the nickname that the bot will associate as. This is required; no default is available.
*
* @param nickname The nickname (case insensitive).
*/
ConnectionBuilder nickname(String nickname);
/**
* Sets the channels that the bot will autoconnect to. If null or empty, no join will be attempted. Default is null.
*
* @param channels A comma-separated list of channels to join, or null.
*/
ConnectionBuilder channels(String channels);
/**
* Sets the channels that the bot will autoconnect to. If null or empty, no join will be attempted. Default is null.
*
* @param channels An array of channels to join, or null.
*/
default ConnectionBuilder channels(String[] channels) {
return channels(channels == null ? null : String.join(",", (CharSequence[]) channels));
}
/**
* Sets the password that will be used to access the server. If null or empty, no password will be sent. Default is null.
*
* @param password The password.
*/
ConnectionBuilder password(String password);
/**
* Sets whether the connection should attempt to reconnect when disconnection is detected. Default is true.
*
* @param autoconnect Whether or not to autoconnect.
*/
ConnectionBuilder autoconnect(boolean autoconnect);
/**
* Sets how long to wait before trying to reconnect, if autoconnect is enabled. Default is 5 seconds.
*
* @param cooldown How long the cooldown period is in milliseconds.
*/
ConnectionBuilder autoconnectCooldown(long cooldown);
/**
* Sets how many times to try reconnecting before giving up. If zero or negative, there is no limit. Default is zero.
*
* @param limit The number of retries allowed.
*/
ConnectionBuilder autoconnectLimit(int limit);
/**
* Validates the arguments, builds the Connection instance and returns it.
* If this is an instance returned by {@link ConnectionManager#addConnection()}, the connection will be added to
* the bot's connection manager. If autoconnect is enabled and the above is true, connection will automatically
* be attempted, otherwise it will not.
*
* @return The built connection.
*/
Connection build();
}
package ninja.nonemu.samurai.connection;
import ninja.nonemu.irc.ConnectionBuilder;
/**
* Manages connections to IRC networks.
* Since multiple simultaneous connections are allowed, this class is provided to allow plugins to add and remove
......@@ -17,39 +15,30 @@ public interface ConnectionManager {
Connection[] getConnections();
/**
* Gets a connection given its name.
* @param name The name of the connection that was given at registration (case insensitive).
* @return The connection found, or null if there is no connection with that name.
*/
Connection getConnection(String name);
/**
* Builds and registers a connection to an IRC server. This does NOT automatically connect to the server.
* Since no name is provided, one is generated from the connection's hostname.
* @param builder A builder that will be responsible for building the connection.
* @return The created connection.
* Gets a connection given its ID string.
* @param id The ID of the connection that was given when it was built. (case insensitive).
* @return The connection found, or null if there is no connection with that ID.
*/
Connection addConnection(ConnectionBuilder builder);
Connection getConnection(String id);
/**
* Builds and registers a connection to an IRC server. This does NOT automatically connect to the server.
* @param name The name of the new connection (case insensitive).
* @param builder A builder that will be responsible for building the connection.
* @return The created connection.
* Creates a builder that will be used to create the new connection.
* When {@link ConnectionBuilder#build()} is called, the newly-created connection will automatically be added.
* @return The new connection builder.
*/
Connection addConnection(String name, ConnectionBuilder builder);
ConnectionBuilder addConnection();
/**
* Removes a connection if it has been registered.
* Removes a connection if it has been added.
* @param connection The connection to remove.
*/
default void removeConnection(Connection connection) {
removeConnection(connection.getName());
removeConnection(connection.getId());
}
/**
* Removes a connection if it has been registered.
* @param name The name of the connection to remove (case insensitive).
* Removes a connection if it has been added.
* @param id The ID string of the connection to remove (case insensitive).
*/
void removeConnection(String name);
void removeConnection(String id);
}
......@@ -16,7 +16,7 @@ public interface PluginManager {
/**
* Registers a plugin extended from the plugin base using its given name.
*
* <br>Equivalent to {@code registerPlugin(plugin, plugin.getName())}.
* <br>Equivalent to {@code registerPlugin(plugin, plugin.getId())}.
* @param plugin The plugin to register.
*/
default void registerPlugin(PluginBase plugin) {
......
package ninja.nonemu.samurai.connection;
import java.util.function.Consumer;
import ninja.nonemu.samurai.BotImpl;
public class ConnectionBuilderImpl implements ConnectionBuilder {
private final BotImpl bot;
private final Consumer<ConnectionImpl> buildCallback;
private String id;
private String host;
private int port;
private String nickname;
private String channels;
private String password;
private boolean autoconnect;
private long cooldown;
private int limit;
public ConnectionBuilderImpl(BotImpl bot, Consumer<ConnectionImpl> buildCallback) {
this.bot = bot;
this.buildCallback = buildCallback;
host = null;
port = 6667;
nickname = "samurai";
channels = null;
password = null;
autoconnect = true;
cooldown = 5000;
limit = 0;
}
@Override
public ConnectionBuilderImpl id(String id) {
this.id = id;
return this;
}
@Override
public ConnectionBuilderImpl host(String host) {
this.host = host;
return this;
}
@Override
public ConnectionBuilderImpl port(int port) {
this.port = port;
return this;
}
@Override
public ConnectionBuilderImpl nickname(String nickname) {
this.nickname = nickname;
return this;
}
@Override
public ConnectionBuilderImpl channels(String channels) {
this.channels = channels;
return this;
}
@Override
public ConnectionBuilderImpl channels(String[] channels) {
return channels(String.join(",", (CharSequence[]) channels));
}
@Override
public ConnectionBuilderImpl password(String password) {
this.password = password;
return this;
}
@Override
public ConnectionBuilderImpl autoconnect(boolean autoconnect) {
this.autoconnect = autoconnect;
return this;
}
@Override
public ConnectionBuilderImpl autoconnectCooldown(long cooldown) {
this.cooldown = cooldown;
return this;
}
@Override
public ConnectionBuilderImpl autoconnectLimit(int limit) {
this.limit = limit;
return this;
}
@Override
public Connection build() {
if (id == null) {
id = host;
}
if (host == null) {
throw new IllegalArgumentException("Hostname cannot be null");
}
if (nickname == null) {
throw new IllegalArgumentException("Nickname cannot be null");
}
ConnectionImpl connection = new ConnectionImpl(
bot,
id,
new ninja.nonemu.irc.Connection(
host,
port,
nickname,
System.getProperty("user.name"),
"Samurai IRC Bot",
password
),
channels,
autoconnect,
cooldown,
limit
);
buildCallback.accept(connection);
return connection;
}
}
......@@ -8,26 +8,57 @@ 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 String name;
private final Logger logger;
private String channels;
private final BotImpl bot;
private final ninja.nonemu.irc.Connection connection;
private final String id;
private final String channels;
private final boolean autoconnect;
private final long cooldown;
private final int limit;
public ConnectionImpl(BotImpl bot, ninja.nonemu.irc.Connection connection) {
this(bot, connection.getHost(), connection);
}
private long lastDisconnect;
private int attempts;
public ConnectionImpl(BotImpl bot, String name, ninja.nonemu.irc.Connection connection) {
public ConnectionImpl(BotImpl bot, String id, ninja.nonemu.irc.Connection connection, String channels, boolean autoconnect, long cooldown, int limit) {
this.bot = bot;
this.name = name;
this.id = id;
this.connection = connection;
logger = bot.getLogger();
channels = null;
this.channels = channels;
this.autoconnect = autoconnect;
this.cooldown = cooldown;
this.limit = limit;
lastDisconnect = -1;
attempts = 0;
}
public void run() {
if (autoconnect && !isConnected()) {
if (lastDisconnect == -1) {
lastDisconnect = System.currentTimeMillis();
attempts = 0;
} else if (System.currentTimeMillis() - lastDisconnect > cooldown) {
try {
connect();
} catch (IOException e) {
attempts++;
if (limit > 0 && attempts >= limit) {
logger.error("Unable to connect to server. Removing from the bot.");
bot.getConnectionManager().removeConnection(this);
} else {
logger.error("Unable to connect to server. Attempting to reconnect when cooldown expires.", e);
}
return;
}
}
}
try {
while (connection.canReadMessage()) {
Message message = readMessage();
......@@ -133,17 +164,13 @@ public class ConnectionImpl implements Connection {
return getUser(name);
}
public void setChannels(String channels) {
this.channels = channels;
}
public ninja.nonemu.irc.Connection getInnerConnection() {
return connection;
}
@Override
public String getName() {
return name;
public String getId() {
return id;
}
@Override
......@@ -158,6 +185,7 @@ public class ConnectionImpl implements Connection {
@Override
public void connect() throws IOException {
lastDisconnect = -1;
connection.connect();
}
......@@ -226,20 +254,4 @@ public class ConnectionImpl implements Connection {
public String getNickname() {
return connection.getNickname();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Connection)) return false;
Connection that = (Connection) o;
return name.equalsIgnoreCase(that.getName());
}
@Override
public int hashCode() {
return name.toLowerCase().hashCode();
}
}
......@@ -9,7 +9,6 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import ninja.nonemu.irc.ConnectionBuilder;
import ninja.nonemu.samurai.BotImpl;
import org.apache.logging.log4j.Logger;
......@@ -86,24 +85,16 @@ public class ConnectionManagerImpl implements ConnectionManager {
}
for (Properties properties : connectionProperties) {
ConnectionBuilder builder = new ConnectionBuilder();
builder.host(properties.getProperty("host"));
if (properties.containsKey("port")) {
builder.port(Integer.parseInt(properties.getProperty("port")));
}
builder.nickname(properties.getProperty("nickname"));
builder.password(properties.getProperty("password"));
ConnectionImpl connection = addConnection(builder);
String channels = properties.getProperty("channels");
if (channels != null && !channels.isEmpty());
connection.setChannels(channels);
try {
connection.connect();
} catch (IOException e) {
logger.error("Unable to connect to server", e);
continue;
}
addConnection()
.host(properties.getProperty("host"))
.port(Integer.parseInt(properties.getProperty("port")))
.nickname(properties.getProperty("nickname"))
.password(properties.getProperty("password"))
.channels(properties.getProperty("channels"))
.autoconnect(Boolean.parseBoolean(properties.getProperty("autoconnect")))
.autoconnectCooldown(Long.parseLong(properties.getProperty("autoconnect_cooldown")))
.autoconnectLimit(Integer.parseInt(properties.getProperty("autoconnect_limit")))
.build();
}
}
......@@ -116,34 +107,22 @@ public class ConnectionManagerImpl implements ConnectionManager {
}
@Override
public ConnectionImpl getConnection(String name) {
return connections.get(name);
}
@Override
public ConnectionImpl addConnection(ConnectionBuilder builder) {
builder.username(System.getProperty("user.name"));
builder.realname("Samurai IRC Bot");
ConnectionImpl connection = new ConnectionImpl(bot, builder.build());
connections.put(connection.getName(), connection);
return connection;
public ConnectionImpl getConnection(String id) {
return connections.get(id);
}
@Override
public Connection addConnection(String name, ConnectionBuilder builder) {
builder.username(System.getProperty("user.name"));
builder.realname("Samurai IRC Bot");
ConnectionImpl connection = new ConnectionImpl(bot, name, builder.build());
connections.put(name, connection);
return connection;
public ConnectionBuilderImpl addConnection() {
return new ConnectionBuilderImpl(bot, connection -> {
if (connections.containsKey(connection.getId())) {
throw new IllegalArgumentException("Connection with that ID has already been added.");
}
connections.put(connection.getId(), connection);
});
}
@Override
public void removeConnection(String name) {
connections.remove(name);
public void removeConnection(String id) {
connections.remove(id);
}
}
......@@ -3,4 +3,7 @@ host=irc.nonemu.ninja
port=6667
nickname=samurai
password=
channels=\#samurai
\ No newline at end of file
channels=\#samurai
autoconnect=true
autoconnect_cooldown=5000
autoconnect_limit=0
\ No newline at end of file
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