/**
* Copyright (C) 2010-2014 Leon Blakey <lord.quackstar at gmail.com>
*
* This file is part of PircBotX.
*
* PircBotX is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* PircBotX is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* PircBotX. If not, see <http://www.gnu.org/licenses/>.
*/
package org.pircbotx;
import static com.google.common.base.Preconditions.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.net.SocketFactory;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.pircbotx.cap.CapHandler;
import org.pircbotx.cap.EnableCapHandler;
import org.pircbotx.dcc.DccHandler;
import org.pircbotx.dcc.ReceiveChat;
import org.pircbotx.dcc.ReceiveFileTransfer;
import org.pircbotx.dcc.SendChat;
import org.pircbotx.dcc.SendFileTransfer;
import org.pircbotx.exception.IrcException;
import org.pircbotx.hooks.CoreHooks;
import org.pircbotx.hooks.Listener;
import org.pircbotx.hooks.managers.ListenerManager;
import org.pircbotx.hooks.managers.ThreadedListenerManager;
import org.pircbotx.output.OutputCAP;
import org.pircbotx.output.OutputChannel;
import org.pircbotx.output.OutputDCC;
import org.pircbotx.output.OutputIRC;
import org.pircbotx.output.OutputRaw;
import org.pircbotx.output.OutputUser;
/**
* Immutable configuration for PircBotX created from
* {@link Configuration.Builder}
*
* @author Leon Blakey
*/
@Data
@ToString(exclude = {"serverPassword", "nickservPassword", "nickservCustomMessage"})
public class Configuration {
//WebIRC
protected final boolean webIrcEnabled;
protected final String webIrcUsername;
protected final String webIrcHostname;
protected final InetAddress webIrcAddress;
protected final String webIrcPassword;
//Bot information
protected final String name;
protected final String login;
protected final String version;
protected final String finger;
protected final String realName;
protected final String channelPrefixes;
protected final String userLevelPrefixes;
protected final boolean snapshotsEnabled;
//DCC
protected final boolean dccFilenameQuotes;
protected final ImmutableList<Integer> dccPorts;
protected final InetAddress dccLocalAddress;
protected final InetAddress dccPublicAddress;
protected final int dccAcceptTimeout;
protected final int dccResumeAcceptTimeout;
protected final int dccTransferBufferSize;
protected final boolean dccPassiveRequest;
//Connect information
protected final ImmutableList<ServerEntry> servers;
protected final String serverPassword;
protected final SocketFactory socketFactory;
protected final InetAddress localAddress;
protected final Charset encoding;
protected final Locale locale;
protected final int socketConnectTimeout;
protected final int socketTimeout;
protected final int maxLineLength;
protected final boolean autoSplitMessage;
protected final boolean autoNickChange;
protected final long messageDelay;
protected final boolean shutdownHookEnabled;
protected final ImmutableMap<String, String> autoJoinChannels;
protected final boolean onJoinWhoEnabled;
protected final boolean identServerEnabled;
protected final String nickservPassword;
protected final String nickservOnSuccess;
protected final String nickservNick;
protected final String nickservCustomMessage;
protected final boolean nickservDelayJoin;
protected final boolean userModeHideRealHost;
protected final boolean autoReconnect;
protected final int autoReconnectDelay;
protected final int autoReconnectAttempts;
//Bot classes
protected final ListenerManager listenerManager;
protected final boolean capEnabled;
protected final ImmutableList<CapHandler> capHandlers;
protected final ImmutableSortedMap<Character, ChannelModeHandler> channelModeHandlers;
protected final BotFactory botFactory;
/**
* Use {@link Configuration.Builder#buildConfiguration() }.
*
* @param builder
* @see Configuration.Builder#buildConfiguration()
*/
protected Configuration(Builder builder) {
//Check for basics
if (builder.isWebIrcEnabled()) {
checkNotNull(builder.getWebIrcAddress(), "Must specify WEBIRC address if enabled");
checkArgument(StringUtils.isNotBlank(builder.getWebIrcHostname()), "Must specify WEBIRC hostname if enabled");
checkArgument(StringUtils.isNotBlank(builder.getWebIrcUsername()), "Must specify WEBIRC username if enabled");
checkArgument(StringUtils.isNotBlank(builder.getWebIrcPassword()), "Must specify WEBIRC password if enabled");
}
checkArgument(StringUtils.isNotBlank(builder.getName()), "Must specify name");
checkArgument(StringUtils.isNotBlank(builder.getLogin()), "Must specify login");
checkArgument(StringUtils.isNotBlank(builder.getVersion()), "Must specify version");
checkArgument(StringUtils.isNotBlank(builder.getFinger()), "Must specify finger");
checkArgument(StringUtils.isNotBlank(builder.getRealName()), "Must specify realName");
checkArgument(StringUtils.isNotBlank(builder.getChannelPrefixes()), "Must specify channel prefixes");
checkNotNull(StringUtils.isNotBlank(builder.getUserLevelPrefixes()), "Channel mode message prefixes cannot be null");
checkNotNull(builder.getDccPorts(), "DCC ports list cannot be null");
checkArgument(builder.getDccAcceptTimeout() > 0, "dccAcceptTimeout must be positive");
checkArgument(builder.getDccResumeAcceptTimeout() > 0, "dccResumeAcceptTimeout must be positive");
checkArgument(builder.getDccTransferBufferSize() > 0, "dccTransferBufferSize must be positive");
checkNotNull(builder.getServers(), "Servers list cannot be null");
checkArgument(!builder.getServers().isEmpty(), "Must specify servers to connect to");
for (ServerEntry serverEntry : builder.getServers()) {
checkArgument(StringUtils.isNotBlank(serverEntry.getHostname()), "Must specify server hostname");
checkArgument(serverEntry.getPort() > 0 && serverEntry.getPort() <= 65535, "Port must be between 1 and 65535");
}
checkNotNull(builder.getSocketFactory(), "Socket factory cannot be null");
checkNotNull(builder.getEncoding(), "Encoding cannot be null");
checkNotNull(builder.getLocale(), "Locale cannot be null");
checkArgument(builder.getSocketConnectTimeout() > 0, "Socket connect timeout must greater than 0");
checkArgument(builder.getSocketTimeout() > 0, "Socket timeout must greater than 0");
checkArgument(builder.getMaxLineLength() > 0, "Max line length must be positive");
checkArgument(builder.getMessageDelay() >= 0, "Message delay must be positive");
checkNotNull(builder.getAutoJoinChannels(), "Auto join channels map cannot be null");
for (Map.Entry<String, String> curEntry : builder.getAutoJoinChannels().entrySet())
if (StringUtils.isBlank(curEntry.getKey()))
throw new RuntimeException("Channel must not be blank");
if (builder.getNickservPassword() != null)
checkArgument(StringUtils.isNotBlank(builder.getNickservPassword()), "Nickserv password cannot be empty");
if (builder.getNickservCustomMessage() != null)
checkArgument(StringUtils.isNotBlank(builder.getNickservCustomMessage()), "Nickserv custom message cannot be empty");
checkArgument(StringUtils.isNotBlank(builder.getNickservOnSuccess()), "Nickserv on success cannot be blank");
checkArgument(StringUtils.isNotBlank(builder.getNickservNick()), "Nickserv nick cannot be blank");
checkArgument(builder.getAutoReconnectAttempts() > 0, "setAutoReconnectAttempts must be greater than 0");
checkArgument(builder.getAutoReconnectDelay() >= 0, "setAutoReconnectDelay must be positive or 0");
checkNotNull(builder.getListenerManager(), "Must specify listener manager");
checkNotNull(builder.getCapHandlers(), "Cap handlers list cannot be null");
checkNotNull(builder.getChannelModeHandlers(), "Channel mode handlers list cannot be null");
checkNotNull(builder.getBotFactory(), "Must specify bot factory");
this.webIrcEnabled = builder.isWebIrcEnabled();
this.webIrcUsername = builder.getWebIrcUsername();
this.webIrcHostname = builder.getWebIrcHostname();
this.webIrcAddress = builder.getWebIrcAddress();
this.webIrcPassword = builder.getWebIrcPassword();
this.name = builder.getName();
this.login = builder.getLogin();
this.version = builder.getVersion();
this.finger = builder.getFinger();
this.realName = builder.getRealName();
this.channelPrefixes = builder.getChannelPrefixes().trim();
this.userLevelPrefixes = builder.getUserLevelPrefixes().trim();
this.snapshotsEnabled = builder.isSnapshotsEnabled();
this.dccFilenameQuotes = builder.isDccFilenameQuotes();
this.dccPorts = ImmutableList.copyOf(builder.getDccPorts());
this.dccLocalAddress = builder.getDccLocalAddress();
this.dccPublicAddress = builder.getDccPublicAddress();
this.dccAcceptTimeout = builder.getDccAcceptTimeout();
this.dccResumeAcceptTimeout = builder.getDccResumeAcceptTimeout();
this.dccTransferBufferSize = builder.getDccTransferBufferSize();
this.dccPassiveRequest = builder.isDccPassiveRequest();
this.servers = ImmutableList.copyOf(builder.getServers());
this.serverPassword = builder.getServerPassword();
this.socketFactory = builder.getSocketFactory();
this.localAddress = builder.getLocalAddress();
this.encoding = builder.getEncoding();
this.locale = builder.getLocale();
this.socketConnectTimeout = builder.getSocketConnectTimeout();
this.socketTimeout = builder.getSocketTimeout();
this.maxLineLength = builder.getMaxLineLength();
this.autoSplitMessage = builder.isAutoSplitMessage();
this.autoNickChange = builder.isAutoNickChange();
this.messageDelay = builder.getMessageDelay();
this.identServerEnabled = builder.isIdentServerEnabled();
this.nickservPassword = builder.getNickservPassword();
this.nickservOnSuccess = builder.getNickservOnSuccess();
this.nickservNick = builder.getNickservNick();
this.nickservCustomMessage = builder.getNickservCustomMessage();
this.nickservDelayJoin = builder.isNickservDelayJoin();
this.userModeHideRealHost = builder.isUserModeHideRealHost();
this.autoReconnect = builder.isAutoReconnect();
this.autoReconnectDelay = builder.getAutoReconnectDelay();
this.autoReconnectAttempts = builder.getAutoReconnectAttempts();
this.listenerManager = builder.getListenerManager();
this.autoJoinChannels = ImmutableMap.copyOf(builder.getAutoJoinChannels());
this.onJoinWhoEnabled = builder.isOnJoinWhoEnabled();
this.capEnabled = builder.isCapEnabled();
this.capHandlers = ImmutableList.copyOf(builder.getCapHandlers());
ImmutableSortedMap.Builder<Character, ChannelModeHandler> channelModeHandlersBuilder = ImmutableSortedMap.naturalOrder();
for (ChannelModeHandler curHandler : builder.getChannelModeHandlers())
channelModeHandlersBuilder.put(curHandler.getMode(), curHandler);
this.channelModeHandlers = channelModeHandlersBuilder.build();
this.shutdownHookEnabled = builder.isShutdownHookEnabled();
this.botFactory = builder.getBotFactory();
}
@SuppressWarnings("unchecked")
public <M extends ListenerManager> M getListenerManager() {
return (M) listenerManager;
}
/**
* Builder to create an immutable {@link Configuration}.
*/
@Accessors(chain = true)
@Data
public static class Builder {
//WebIRC
/**
* Enable or disable sending WEBIRC line on connect, default disabled
*/
protected boolean webIrcEnabled = false;
/**
* Username of WEBIRC connection, must not be blank if WEBIRC is enabled
*/
protected String webIrcUsername = null;
/**
* Hostname of WEBIRC connection, must not be blank if WEBIRC is enabled
*/
protected String webIrcHostname = null;
/**
* IP address of WEBIRC connection, must be set if WEBIRC is enabled
*/
protected InetAddress webIrcAddress = null;
/**
* Password of WEBIRC connection, must not be blank if WEBIRC is enabled
*/
protected String webIrcPassword = null;
//Bot information
/**
* The nick to be used for the IRC connection (nick!login@host), must
* not be blank
*/
protected String name;
/**
* The login to be used for the IRC connection (nick!login@host),
* default PircBotX
*/
protected String login = "PircBotX";
/**
* CTCP version response.
*/
protected String version = "PircBotX " + PircBotX.VERSION + " Java IRC bot - github.com/thelq/pircbotx";
/**
* CTCP finger response
*/
protected String finger = "You ought to be arrested for fingering a bot!";
/**
* The realName/fullname used for WHOIS info, defaults to version
*/
protected String realName = version;
/**
* Allowed channel prefix characters, default <code>#&+!</code>
*/
protected String channelPrefixes = "#&+!";
/**
* Supported channel prefixes that restrict a sent message to users with
* this mode, eg <code>PRIVMSG +#channel :hello</code> will only send a
* message to voiced or higher users, default <code>+@%&~!</code>
*/
protected String userLevelPrefixes = UserLevel.getSymbols() + "!";
/**
* Enable creation of snapshots, default true. In bulk datasets or very
* lower power devices, creating snapshots can be a relatively expensive
* operation for every
* {@link org.pircbotx.hooks.types.GenericSnapshotEvent} (eg PartEvent,
* QuitEvent) since the entire UserChannelDao with all of its users and
* channels is cloned. This can optionally disabled by setting this to
* false, however this makes all
* {@link org.pircbotx.hooks.types.GenericSnapshotEvent#getUserChannelDaoSnapshot()}
* calls return null.
* <p>
* In regular usage disabling snapshots is not necessary because there
* relatively few user QUITs and PARTs per second.
*/
protected boolean snapshotsEnabled = true;
//DCC
/**
* If true sends filenames in quotes, otherwise uses underscores,
* default enabled.
*/
protected boolean dccFilenameQuotes = false;
/**
* Ports to allow DCC incoming connections, recommended to set multiple
* as DCC connections will be rejected if no free port can be found
*/
protected List<Integer> dccPorts = Lists.newArrayList();
/**
* The local address to bind DCC connections to, defaults to null (which
* will be figured out at runtime)
*/
protected InetAddress dccLocalAddress = null;
/**
* The public address advertised to other users, defaults to null (which
* will be figured out at runtime)
*/
protected InetAddress dccPublicAddress = null;
/**
* Timeout for user to accept a sent DCC request, defaults to {@link #getSocketTimeout()
* }
*/
protected int dccAcceptTimeout = -1;
/**
* Timeout for a user to accept a resumed DCC request, defaults to {@link #getDccResumeAcceptTimeout()
* }
*/
protected int dccResumeAcceptTimeout = -1;
/**
* Size of the DCC file transfer buffer, default 1024 bytes
*/
protected int dccTransferBufferSize = 1024;
/**
* Send DCC requests as passive/reverse requests if not specified
* otherwise, default false
*/
protected boolean dccPassiveRequest = false;
//Connect information
/**
* List of servers to connect to, easily add with the addServer methods
*/
protected List<ServerEntry> servers = Lists.newLinkedList();
/**
* Password for IRC server, default null
*/
protected String serverPassword = null;
/**
* Socket factory for connections, defaults to {@link SocketFactory#getDefault()
* }
*/
protected SocketFactory socketFactory = SocketFactory.getDefault();
/**
* Address to bind to when connecting to IRC server, default null
*/
protected InetAddress localAddress = null;
/**
* Charset encoding to use for connection, defaults to
* {@link Charset#defaultCharset()}
*/
protected Charset encoding = Charset.defaultCharset();
/**
* Locale to use for connection, defaults to {@link Locale#getDefault()
* }
*/
protected Locale locale = Locale.getDefault();
/**
* Milliseconds to wait to connect to an IRC server address before
* trying the next address, default {@link #getSocketTimeout() }
*/
protected int socketConnectTimeout = -1;
/**
* Milliseconds to wait with no data from the IRC server before sending
* a PING request to check if the socket is still alive, default 5
* minutes (1000x60x5=300,000 milliseconds)
*/
protected int socketTimeout = 1000 * 60 * 5;
/**
* Maximum line length of IRC server, defaults 512 characters
*/
protected int maxLineLength = 512;
/**
* Enable or disable automatic message splitting to fit
* {@link #getMaxLineLength()} to prevent the IRC server from possibly
* truncating or rejecting the line, default true.
*/
protected boolean autoSplitMessage = true;
/**
* Enable or disable automatic nick changing if a nick is in use by
* adding a number to the end, default false which will throw a
* {@link IrcException} if the nick is already in use on the server
*/
protected boolean autoNickChange = false;
/**
* Millisecond delay between sending messages, default 1000 milliseconds
*/
protected long messageDelay = 1000;
/**
* Enable or disable creating a JVM shutdown hook which will properly
* QUIT the IRC server and shutdown the bot, default true
*/
protected boolean shutdownHookEnabled = true;
/**
* Map of channels and keys to automatically join upon connecting.
*/
protected final Map<String, String> autoJoinChannels = Maps.newHashMap();
/**
* Enable or disable sending "WHO #channel" upon joining a channel and
* rely only on the NAMES response
*/
protected boolean onJoinWhoEnabled = true;
/**
* Enable or disable use of an existing {@link IdentServer}, default
* false. Note that the IdentServer must be started separately or else
* an exception will be thrown
*
* @see IdentServer
*/
protected boolean identServerEnabled = false;
/**
* Password to authenticate against NICKSERV, default null (will not try
* to identify)
*/
protected String nickservPassword = null;
/**
* Case-insensitive message a user with
* {@link #setNickservNick(java.lang.String) } in its hostmask will
* always contain when we have successfully identified, defaults to "you
* are now" which which matches all of the following known server
* responses:
* <ul>
* <li>ircd-seven (freenode) - You are now identified for PircBotX</li>
* <li>Unreal (swiftirc) - Password accepted - you are now
* recognized.</li>
* <li>InspIRCd (mozilla) - You are now logged in as PircBotX</li>
* </ul>
*
* @see PircBotX#isNickservIdentified()
* @see #setNickservNick(java.lang.String)
*/
protected String nickservOnSuccess = "you are now";
/**
* The nick of the nickserv service account, default "nickserv".
*
* @see PircBotX#isNickservIdentified()
*/
protected String nickservNick = "nickserv";
/**
* Some irc servers require a custom identify string.
* eg: Quakenet: <code>PRIVMSG Q@CServe.quakenet.org :AUTH USER PASS</code>
* default = null
*/
protected String nickservCustomMessage = null;
/**
* Delay joining channels until were identified to nickserv, default
* false
*/
protected boolean nickservDelayJoin = false;
/**
* Sets mode +x on the bot, to hide the real hostname, default = false
*/
protected boolean userModeHideRealHost = false;
/**
* Enable or disable automatic reconnecting, default false. Note that
* you MUST call {@link PircBotX#stopBotReconnect() } when you do not
* want the bot to reconnect anymore!
*/
protected boolean autoReconnect = false;
/**
* Delay in milliseconds between reconnect attempts, default 0.
*/
protected int autoReconnectDelay = 0;
/**
* Number of times to attempt to reconnect, default 5.
*/
protected int autoReconnectAttempts = 5;
//Bot classes
/**
* The {@link ListenerManager} to use to handle events, default
* {@link ThreadedListenerManager}.
*/
//This is lazy loaded in {@link #getListenerManager()} since creating a thread pool is expensive
protected ListenerManager listenerManager = null;
/**
* Enable or disable CAP handling, defaults true.
*/
protected boolean capEnabled = true;
/**
* IRCv3 CAP features to try to use, default enables multi-prefix and
* away-notify but ignoring if the server doesn't support them
*/
protected final List<CapHandler> capHandlers = Lists.<CapHandler>newArrayList(
new EnableCapHandler("multi-prefix", true),
new EnableCapHandler("away-notify", true)
);
/**
* Handlers for channel modes, defaults to built-in handlers which cover
* basic modes that are generally supported on most IRC servers
*/
protected final List<ChannelModeHandler> channelModeHandlers = Lists.newArrayList(InputParser.DEFAULT_CHANNEL_MODE_HANDLERS);
/**
* The {@link BotFactory} to use
*/
protected BotFactory botFactory = new BotFactory();
/**
* Create with defaults that work in most situations and IRC servers
*/
public Builder() {
}
/**
* Copy values from an existing Configuration.
*
* @param configuration Configuration to copy values from
*/
public Builder(Configuration configuration) {
this.webIrcEnabled = configuration.isWebIrcEnabled();
this.webIrcUsername = configuration.getWebIrcUsername();
this.webIrcHostname = configuration.getWebIrcHostname();
this.webIrcAddress = configuration.getWebIrcAddress();
this.webIrcPassword = configuration.getWebIrcPassword();
this.name = configuration.getName();
this.login = configuration.getLogin();
this.version = configuration.getVersion();
this.finger = configuration.getFinger();
this.realName = configuration.getRealName();
this.channelPrefixes = configuration.getChannelPrefixes();
this.userLevelPrefixes = configuration.getUserLevelPrefixes();
this.snapshotsEnabled = configuration.isSnapshotsEnabled();
this.dccFilenameQuotes = configuration.isDccFilenameQuotes();
this.dccPorts.clear();
this.dccPorts.addAll(configuration.getDccPorts());
this.dccLocalAddress = configuration.getDccLocalAddress();
this.dccPublicAddress = configuration.getDccPublicAddress();
this.dccAcceptTimeout = configuration.getDccAcceptTimeout();
this.dccResumeAcceptTimeout = configuration.getDccResumeAcceptTimeout();
this.dccTransferBufferSize = configuration.getDccTransferBufferSize();
this.dccPassiveRequest = configuration.isDccPassiveRequest();
this.servers.clear();
this.servers.addAll(configuration.getServers());
this.serverPassword = configuration.getServerPassword();
this.socketFactory = configuration.getSocketFactory();
this.localAddress = configuration.getLocalAddress();
this.encoding = configuration.getEncoding();
this.locale = configuration.getLocale();
this.socketConnectTimeout = configuration.getSocketConnectTimeout();
this.socketTimeout = configuration.getSocketTimeout();
this.maxLineLength = configuration.getMaxLineLength();
this.autoSplitMessage = configuration.isAutoSplitMessage();
this.autoNickChange = configuration.isAutoNickChange();
this.messageDelay = configuration.getMessageDelay();
this.listenerManager = configuration.getListenerManager();
this.nickservPassword = configuration.getNickservPassword();
this.nickservOnSuccess = configuration.getNickservOnSuccess();
this.nickservNick = configuration.getNickservNick();
this.nickservCustomMessage = configuration.getNickservCustomMessage();
this.nickservDelayJoin = configuration.isNickservDelayJoin();
this.userModeHideRealHost = configuration.isUserModeHideRealHost();
this.autoReconnect = configuration.isAutoReconnect();
this.autoReconnectDelay = configuration.getAutoReconnectDelay();
this.autoReconnectAttempts = configuration.getAutoReconnectAttempts();
this.autoJoinChannels.clear();
this.autoJoinChannels.putAll(configuration.getAutoJoinChannels());
this.onJoinWhoEnabled = configuration.isOnJoinWhoEnabled();
this.identServerEnabled = configuration.isIdentServerEnabled();
this.capEnabled = configuration.isCapEnabled();
this.capHandlers.clear();
this.capHandlers.addAll(configuration.getCapHandlers());
this.channelModeHandlers.clear();
this.channelModeHandlers.addAll(configuration.getChannelModeHandlers().values());
this.shutdownHookEnabled = configuration.isShutdownHookEnabled();
this.botFactory = configuration.getBotFactory();
}
/**
* Copy values from another builder.
*
* @param otherBuilder
*/
public Builder(Builder otherBuilder) {
this.webIrcEnabled = otherBuilder.isWebIrcEnabled();
this.webIrcUsername = otherBuilder.getWebIrcUsername();
this.webIrcHostname = otherBuilder.getWebIrcHostname();
this.webIrcAddress = otherBuilder.getWebIrcAddress();
this.webIrcPassword = otherBuilder.getWebIrcPassword();
this.name = otherBuilder.getName();
this.login = otherBuilder.getLogin();
this.version = otherBuilder.getVersion();
this.finger = otherBuilder.getFinger();
this.realName = otherBuilder.getRealName();
this.channelPrefixes = otherBuilder.getChannelPrefixes();
this.userLevelPrefixes = otherBuilder.getUserLevelPrefixes();
this.snapshotsEnabled = otherBuilder.isSnapshotsEnabled();
this.dccFilenameQuotes = otherBuilder.isDccFilenameQuotes();
this.dccPorts.clear();
this.dccPorts.addAll(otherBuilder.getDccPorts());
this.dccLocalAddress = otherBuilder.getDccLocalAddress();
this.dccPublicAddress = otherBuilder.getDccPublicAddress();
this.dccAcceptTimeout = otherBuilder.getDccAcceptTimeout();
this.dccResumeAcceptTimeout = otherBuilder.getDccResumeAcceptTimeout();
this.dccTransferBufferSize = otherBuilder.getDccTransferBufferSize();
this.dccPassiveRequest = otherBuilder.isDccPassiveRequest();
this.servers.clear();
this.servers.addAll(otherBuilder.getServers());
this.serverPassword = otherBuilder.getServerPassword();
this.socketFactory = otherBuilder.getSocketFactory();
this.localAddress = otherBuilder.getLocalAddress();
this.encoding = otherBuilder.getEncoding();
this.locale = otherBuilder.getLocale();
this.socketConnectTimeout = otherBuilder.getSocketConnectTimeout();
this.socketTimeout = otherBuilder.getSocketTimeout();
this.maxLineLength = otherBuilder.getMaxLineLength();
this.autoSplitMessage = otherBuilder.isAutoSplitMessage();
this.autoNickChange = otherBuilder.isAutoNickChange();
this.messageDelay = otherBuilder.getMessageDelay();
this.listenerManager = otherBuilder.getListenerManager();
this.nickservPassword = otherBuilder.getNickservPassword();
this.nickservOnSuccess = otherBuilder.getNickservOnSuccess();
this.nickservNick = otherBuilder.getNickservNick();
this.nickservCustomMessage = otherBuilder.getNickservCustomMessage();
this.nickservDelayJoin = otherBuilder.isNickservDelayJoin();
this.userModeHideRealHost = otherBuilder.isUserModeHideRealHost();
this.autoReconnect = otherBuilder.isAutoReconnect();
this.autoReconnectDelay = otherBuilder.getAutoReconnectDelay();
this.autoReconnectAttempts = otherBuilder.getAutoReconnectAttempts();
this.autoJoinChannels.putAll(otherBuilder.getAutoJoinChannels());
this.onJoinWhoEnabled = otherBuilder.isOnJoinWhoEnabled();
this.identServerEnabled = otherBuilder.isIdentServerEnabled();
this.capEnabled = otherBuilder.isCapEnabled();
this.capHandlers.clear();
this.capHandlers.addAll(otherBuilder.getCapHandlers());
this.channelModeHandlers.clear();
this.channelModeHandlers.addAll(otherBuilder.getChannelModeHandlers());
this.shutdownHookEnabled = otherBuilder.isShutdownHookEnabled();
this.botFactory = otherBuilder.getBotFactory();
}
public int getSocketConnectTimeout() {
return (socketConnectTimeout != -1) ? socketConnectTimeout : socketTimeout;
}
/**
* Timeout for user to accept a sent DCC request. Defaults to {@link #getSocketTimeout()
* }
*/
public int getDccAcceptTimeout() {
return (dccAcceptTimeout != -1) ? dccAcceptTimeout : socketTimeout;
}
/**
* Timeout for a user to accept a resumed DCC request. Defaults to {@link #getDccResumeAcceptTimeout()
* }
*/
public int getDccResumeAcceptTimeout() {
return (dccResumeAcceptTimeout != -1) ? dccResumeAcceptTimeout : getDccAcceptTimeout();
}
/**
* Add a collection of cap handlers
*
* @see #getCapHandlers()
* @param handlers
*/
public Builder addCapHandlers(@NonNull Iterable<CapHandler> handlers) {
for (CapHandler curHandler : handlers) {
addCapHandler(curHandler);
}
return this;
}
/**
* Add a cap handler
*
* @see #getCapHandlers()
* @param handler
*/
public Builder addCapHandler(CapHandler handler) {
getCapHandlers().add(handler);
return this;
}
/**
* Add a collection of listeners to the current ListenerManager
*
* @see #getListenerManager()
* @param listeners
*/
public Builder addListeners(@NonNull Iterable<Listener> listeners) {
for (Listener curListener : listeners) {
addListener(curListener);
}
return this;
}
/**
* Add a listener to the current ListenerManager
*
* @see #getListenerManager()
* @param listener
*/
public Builder addListener(Listener listener) {
getListenerManager().addListener(listener);
return this;
}
public Builder addAutoJoinChannels(@NonNull Iterable<String> channels) {
for (String curChannel : channels) {
addAutoJoinChannel(curChannel);
}
return this;
}
/**
* Add a channel to join on connect
*
* @see #getAutoJoinChannels()
* @param channel
*/
public Builder addAutoJoinChannel(@NonNull String channel) {
if (StringUtils.isBlank(channel))
throw new RuntimeException("Channel must not be blank");
getAutoJoinChannels().put(channel, "");
return this;
}
/**
* Utility method for <code>{@link #getAutoJoinChannels()}.put(channel,
* key)</code>
*
* @param channel
*/
public Builder addAutoJoinChannel(@NonNull String channel, @NonNull String key) {
if (StringUtils.isBlank(channel))
throw new RuntimeException("Channel must not be blank");
if (StringUtils.isBlank(key))
throw new RuntimeException("Key must not be blank");
getAutoJoinChannels().put(channel, key);
return this;
}
//TODO: Temporary backwards compatibility
private void checkSetServerBackwardsCompatible() {
if(servers.size() >= 2)
throw new RuntimeException("Cannot combine deprecated setServer and addServer");
}
/**
* @deprecated Use {@link #addServer(java.lang.String)},
* will be removed in future releases
*/
@Deprecated
public Builder setServer(String hostname) {
checkSetServerBackwardsCompatible();
servers.clear();
servers.add(new ServerEntry(hostname, 6667));
return this;
}
/**
* @deprecated Use {@link #addServer(java.lang.String, int)},
* will be removed in future releases
*/
@Deprecated
public Builder setServer(String hostname, int port) {
checkSetServerBackwardsCompatible();
servers.clear();
servers.add(new ServerEntry(hostname, 6667));
return this;
}
/**
* @deprecated Use {@link #addServer(java.lang.String)},
* will be removed in future releases
*/
@Deprecated
public Builder setServerHostname(String hostname) {
checkSetServerBackwardsCompatible();
if(servers.size() == 1)
servers.add(new ServerEntry(hostname, servers.remove(0).port));
else
servers.add(new ServerEntry(hostname, 6667));
return this;
}
/**
* @deprecated Use {@link #addServer(java.lang.String, int)},
* will be removed in future releases
*/
@Deprecated
public Builder setServerPort(int port) {
checkSetServerBackwardsCompatible();
if(servers.size() == 1)
servers.add(new ServerEntry(servers.remove(0).hostname, port));
else
servers.add(new ServerEntry("unset", port));
return this;
}
public Builder addServer(@NonNull String server) {
servers.add(new ServerEntry(server, 6667));
return this;
}
public Builder addServer(@NonNull String server, int port) {
servers.add(new ServerEntry(server, port));
return this;
}
public Builder addServer(@NonNull ServerEntry serverEntry) {
servers.add(serverEntry);
return this;
}
public Builder addServers(@NonNull Iterable<ServerEntry> serverEnteries) {
for (ServerEntry curServerEntry : serverEnteries)
servers.add(curServerEntry);
return this;
}
/**
* Sets a new ListenerManager. <b>NOTE:</b> The {@link CoreHooks} are
* added when this method is called. If you do not want this, remove
* CoreHooks with
* {@link ListenerManager#removeListener(org.pircbotx.hooks.Listener) }
*
* @param listenerManager The listener manager
*/
@SuppressWarnings("unchecked")
public Builder setListenerManager(ListenerManager listenerManager) {
this.listenerManager = listenerManager;
for (Listener curListener : this.listenerManager.getListeners())
if (curListener instanceof CoreHooks)
return this;
listenerManager.addListener(new CoreHooks());
return this;
}
public void replaceCoreHooksListener(CoreHooks extended) {
//Find the corehooks impl
CoreHooks orig = null;
for (Listener curListener : this.listenerManager.getListeners())
if (curListener instanceof CoreHooks)
orig = (CoreHooks) curListener;
//Swap
if (orig != null)
this.listenerManager.removeListener(orig);
addListener(extended);
}
/**
* Returns the current ListenerManager in use by this bot. Note that the
* default listener manager ({@link ListenerManager}) is lazy loaded
* here unless one was already set
*
* @return Current ListenerManager
*/
@SuppressWarnings("unchecked")
public <M extends ListenerManager> M getListenerManager() {
if (listenerManager == null)
setListenerManager(new ThreadedListenerManager());
return (M) listenerManager;
}
/**
* Builds a Configuration instance from the information in this builder
*/
public Configuration buildConfiguration() {
return new Configuration(this);
}
/**
* Create a <i>new</i> builder with the specified hostname then build a
* configuration. Useful for template builders
*
* @param hostname
*/
public Configuration buildForServer(String hostname) {
return new Builder(this)
.addServer(hostname)
.buildConfiguration();
}
/**
* Create a <i>new</i> builder with the specified hostname and port then
* build a configuration. Useful for template builders
*
* @param hostname
*/
public Configuration buildForServer(String hostname, int port) {
return new Builder(this)
.addServer(hostname, port)
.buildConfiguration();
}
/**
* Create a <i>new</i> builder with the specified hostname, port, and
* password then build a configuration. Useful for template builders
*
* @param hostname
*/
public Configuration buildForServer(String hostname, int port, String password) {
return new Builder(this)
.addServer(hostname, port)
.setServerPassword(password)
.buildConfiguration();
}
}
/**
* Factory for various internal bot classes.
*/
public static class BotFactory {
//Allow subclasses to use own version of User and Channel
@SuppressWarnings("rawtypes")
public UserChannelDao createUserChannelDao(PircBotX bot) {
return new UserChannelDao(bot, bot.getConfiguration().getBotFactory());
}
public OutputRaw createOutputRaw(PircBotX bot) {
return new OutputRaw(bot);
}
public OutputCAP createOutputCAP(PircBotX bot) {
return new OutputCAP(bot);
}
public OutputIRC createOutputIRC(PircBotX bot) {
return new OutputIRC(bot);
}
public OutputDCC createOutputDCC(PircBotX bot) {
return new OutputDCC(bot);
}
public OutputChannel createOutputChannel(PircBotX bot, Channel channel) {
return new OutputChannel(bot, channel);
}
public OutputUser createOutputUser(PircBotX bot, UserHostmask user) {
return new OutputUser(bot, user);
}
public InputParser createInputParser(PircBotX bot) {
return new InputParser(bot);
}
public DccHandler createDccHandler(PircBotX bot) {
return new DccHandler(bot);
}
public SendChat createSendChat(PircBotX bot, User user, Socket socket) throws IOException {
return new SendChat(user, socket, bot.getConfiguration().getEncoding());
}
public ReceiveChat createReceiveChat(PircBotX bot, User user, Socket socket) throws IOException {
return new ReceiveChat(user, socket, bot.getConfiguration().getEncoding());
}
public SendFileTransfer createSendFileTransfer(PircBotX bot, Socket socket, User user, File file, long startPosition) {
return new SendFileTransfer(bot.getConfiguration(), socket, user, file, startPosition);
}
public ReceiveFileTransfer createReceiveFileTransfer(PircBotX bot, Socket socket, User user, File file, long startPosition, long fileSize) {
return new ReceiveFileTransfer(bot.getConfiguration(), socket, user, file, startPosition, fileSize);
}
public ServerInfo createServerInfo(PircBotX bot) {
return new ServerInfo(bot);
}
public UserHostmask createUserHostmask(PircBotX bot, String hostmask) {
return new UserHostmask(bot, hostmask);
}
public UserHostmask createUserHostmask(PircBotX bot, String extbanPrefix, String nick, String login, String hostname) {
return new UserHostmask(bot, extbanPrefix, nick, login, hostname);
}
public User createUser(UserHostmask userHostmask) {
return new User(userHostmask);
}
public Channel createChannel(PircBotX bot, String name) {
return new Channel(bot, name);
}
}
@Data
public static class ServerEntry {
@NonNull
private final String hostname;
private final int port;
@Override
public String toString() {
return hostname + ":" + port;
}
}
}