summaryrefslogtreecommitdiffstats
path: root/EssentialsUpdate/src/f00f/net/irc/martyr/services
diff options
context:
space:
mode:
authorsnowleo <schneeleo@gmail.com>2011-10-12 03:14:07 +0200
committersnowleo <schneeleo@gmail.com>2011-10-12 03:14:26 +0200
commit860d446d28776ec842fa53e8e08538d4e093d6e9 (patch)
tree0c4598eae4eb8c59fd36e8312eab1b27a8018794 /EssentialsUpdate/src/f00f/net/irc/martyr/services
parent9ec398b39b0f48392a9d635041b392c7dba2ca0c (diff)
downloadEssentials-860d446d28776ec842fa53e8e08538d4e093d6e9.tar
Essentials-860d446d28776ec842fa53e8e08538d4e093d6e9.tar.gz
Essentials-860d446d28776ec842fa53e8e08538d4e093d6e9.tar.lz
Essentials-860d446d28776ec842fa53e8e08538d4e093d6e9.tar.xz
Essentials-860d446d28776ec842fa53e8e08538d4e093d6e9.zip
EssentialsUpdate WIP
Diffstat (limited to 'EssentialsUpdate/src/f00f/net/irc/martyr/services')
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoJoin.java157
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java271
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoRegister.java289
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoResponder.java82
4 files changed, 799 insertions, 0 deletions
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoJoin.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoJoin.java
new file mode 100644
index 000000000..786552a7b
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoJoin.java
@@ -0,0 +1,157 @@
+/*
+ * Original version: Ben Damm <bdamm@dammfine.com>
+ * Changes by: Morgan Christiansson <martyr@mog.se>
+ * - Spotted bugs
+ * - Added timer
+ * - Responds to Invites
+ * - Re-tries a join with a bad key
+ *
+ * Note: Requires Java 1.4
+ */
+package f00f.net.irc.martyr.services;
+
+import f00f.net.irc.martyr.GenericAutoService;
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.TimerTaskCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.commands.InviteCommand;
+import f00f.net.irc.martyr.commands.JoinCommand;
+import f00f.net.irc.martyr.commands.KickCommand;
+import f00f.net.irc.martyr.errors.GenericJoinError;
+
+/**
+ * <p>AutoJoin joins a group if the IRCConnection is ready. It will wait until
+ * it is ready if it is not (by waiting for the REGISTERED state change).</p>
+ *
+ * <p>AutoJoin maintains a persistent Join (re-join if kicked).
+ * AutoJoin can cease to be persistent by calling the 'disable'
+ * method.</p>
+ */
+public class AutoJoin extends GenericAutoService
+{
+ //static Logger log = Logger.getLogger(AutoJoin.class);
+
+ private String channel = null;
+ private String key = null;
+ private TimerTaskCommand joinTimerTask = null;
+ private long joinTimerTaskDelay = 10*1000;
+
+ public AutoJoin( IRCConnection connection, String channel )
+ {
+ this( connection, channel, null );
+ }
+
+ public AutoJoin( IRCConnection connection, String channel, String key )
+ {
+ super( connection );
+
+ this.channel = channel;
+ this.key = key;
+
+ enable();
+
+ updateState( connection.getState() );
+ }
+
+ protected void updateState( State state )
+ {
+
+ if( state == State.REGISTERED )
+ performJoin();
+ }
+
+ protected void updateCommand( InCommand command_o )
+ {
+ if( command_o instanceof KickCommand )
+ {
+ KickCommand kickCommand = (KickCommand)command_o;
+
+ if( kickCommand.kickedUs( getConnection().getClientState() ) )
+ {
+ if( Channel.areEqual(kickCommand.getChannel(), channel))
+ {
+ performJoin();
+ }
+ else
+ {
+ // mog: TODO: Should we really join a channel for which we aren't the AutoJoin:er?
+ // BD: You are quite right, this AutoJoin should only worry about itself.
+ // getConnection().sendCommand( new JoinCommand( kickCommand.getChannel() ) );
+ }
+ }
+ }
+ else if(command_o instanceof GenericJoinError )
+ {
+ GenericJoinError joinErr = (GenericJoinError)command_o;
+
+ if( Channel.areEqual( joinErr.getChannel(), channel ) )
+ {
+ //log.debug("AutoJoin: Failed to join channel: "+joinErr.getComment());
+ scheduleJoin();
+ }
+ }
+ else if( command_o instanceof InviteCommand )
+ {
+ InviteCommand invite = (InviteCommand)command_o;
+ if(!getConnection().getClientState().isOnChannel(invite.getChannel()))
+ {
+ performJoin();
+ }
+ }
+ }
+
+ /**
+ * Sets up and sends the join command.
+ * */
+ protected synchronized void performJoin()
+ {
+ setupJoin();
+ sendJoinCommand();
+ }
+
+ /**
+ * Performs various tasks immediatly prior to sending a join command.
+ * Called from performJoin.
+ * */
+ protected void setupJoin()
+ {
+ if(joinTimerTask != null)
+ {
+ joinTimerTask.cancel();
+ joinTimerTask = null;
+ }
+ }
+
+ /**
+ * This method sends the actual command. Called from performJoin.
+ * */
+ protected void sendJoinCommand()
+ {
+ getConnection().sendCommand( new JoinCommand( channel, key ) );
+ }
+
+ protected void scheduleJoin()
+ {
+ if(joinTimerTask == null || !joinTimerTask.isScheduled())
+ {
+ joinTimerTask = new TimerTaskCommand(getConnection(), new JoinCommand(channel, key));
+ //TODO back off delay on repeated retries?
+ getConnection().getCronManager().schedule(joinTimerTask, joinTimerTaskDelay);
+ }
+ }
+
+ public String toString()
+ {
+ if( key == null )
+ return "AutoJoin [" + channel + "]";
+ return "AutoJoin [" + channel + "," + key + "]";
+ }
+
+ // END AutoResponder
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java
new file mode 100644
index 000000000..06ff33f70
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java
@@ -0,0 +1,271 @@
+package f00f.net.irc.martyr.services;
+
+import java.io.IOException;
+
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.GenericAutoService;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.commands.QuitCommand;
+
+/**
+ * <p>AutoReconnect performs the job of reconnecting to the server, if
+ * the connection is terminated unexpectedly. AutoReconnect
+ * will try to reconnect 5 times, and then give up. If AutoReconnect
+ * intercepts a QUIT command before the state change that is issued by
+ * us (bounced back from the server) then it will not try to
+ * reconnect.</p>
+ *
+ * <p>Note that AutoReconnect has no play in nick negotiation, and as
+ * such, a failed nick negotiation does not count as a connection
+ * retry.</p>
+ *
+ * <p>Testing note: Does a server send a QUIT before a forceful
+ * removal from the network? Should AutoReconnect not intercept
+ * QUITs? Certainly not all servers send QUITs even when you QUIT on
+ * your own; this class should be put into a command-out chain as well.</p>
+ */
+public class AutoReconnect extends GenericAutoService
+{
+ //static Logger log = Logger.getLogger(AutoReconnect.class);
+
+ private int attempt;
+ private int maxAttempts;
+ private int sleepTime;
+ private boolean disableOnQuit;
+
+ // How many times to try reconnecting?
+ public static final int DEFAULT_MAX_ATTEMPTS = 5;
+ // TODO This is a rather simplistic method, personally I would like to
+ // see a version of this class that implements a backoff algorithm.
+
+ // If we tried to connect, but failed, how long do we wait until we
+ // try again (ms)?
+ public static final int DEFAULT_CONNECT_SLEEPTIME = 1000;
+
+ // If we get a QUIT command from the server notifying us that we have
+ // QUIT, then self-disable so that we don't reconnect.
+ public static final boolean DEFAULT_DISABLE_ON_QUIT = true;
+
+ /**
+ * @param connection The IRCConnection
+ * @param maxAttempts The number of tries to do before giving up
+ * @param sleepBetween Milliseconds to sleep between retries
+ * @param disableOnQuit Automatically disable on quit event
+ */
+ public AutoReconnect( IRCConnection connection, int maxAttempts,
+ int sleepBetween, boolean disableOnQuit )
+ {
+ super( connection );
+
+ this.disableOnQuit = disableOnQuit;
+ this.maxAttempts = maxAttempts;
+ this.sleepTime = sleepBetween;
+ this.attempt = 0;
+
+ enable();
+ }
+
+ public AutoReconnect( IRCConnection connection, int maxAttempts,
+ int sleepBetween )
+ {
+ this( connection, maxAttempts, sleepBetween, DEFAULT_DISABLE_ON_QUIT );
+ }
+
+ /**
+ * Initializes with reasonable defaults.
+ *
+ * @param connection Connection we are associated with
+ */
+ public AutoReconnect( IRCConnection connection )
+ {
+ this( connection, DEFAULT_MAX_ATTEMPTS, DEFAULT_CONNECT_SLEEPTIME );
+ }
+
+ /**
+ * Attempts to connect, returning only when a connection has been made
+ * or repeated connections have failed.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * */
+ public void go( String server, int port )
+ {
+ doConnectionLoop( server, port );
+ }
+
+ /**
+ * Attempts a single connection to the server. The connection is done
+ * by asking the client state what server and port we are supposed to
+ * be connected to, and then calling IRCConnection.connect(...).
+ *
+ * @throws IOException if we could not connect successfully
+ * */
+ protected void connect()
+ throws IOException
+ {
+ ClientState cstate = getConnection().getClientState();
+ connect( cstate.getServer(), cstate.getPort() );
+ }
+
+ /**
+ * Attempts a single connection to the server.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * @throws IOException if we could not connect successfully
+ * */
+ protected void connect( String server, int port )
+ throws IOException
+ {
+ getConnection().connect( server, port );
+ }
+
+ protected void updateState( State state )
+ {
+ //log.debug("AutoReconnect: Update with state " + state);
+ if( state == State.UNCONNECTED )
+ {
+ // This should only happen if we were connected and then
+ // disconnected. Try connecting.
+
+ // failedToConnect() prevents insane-reconnecting loop if
+ // we never registered, however, it introduces a delay if we
+ // were registered properly previously
+ if (failedToConnect(null))
+ doConnectionLoop();
+ } else if( state == State.REGISTERED ) {
+ this.attempt = 0;
+ }
+
+ //log.debug("AutoReconnect: Returned from " + state);
+ }
+
+ /**
+ * Calls connect() followed by failedToConnect(...) until a connection
+ * is made. Gets the server and port from the client state, implying
+ * that a connection was already attempted before.
+ * */
+ protected void doConnectionLoop()
+ {
+ // Tell called proc to use client state info
+ doConnectionLoop( null, -1 );
+ }
+
+ /**
+ * Calls connect() followed by failedToConnect(...) until a connection
+ * is made, or the service is disabled.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * */
+ protected void doConnectionLoop( String server, int port )
+ {
+ boolean keeptrying = true;
+
+ while( keeptrying && enabled )
+ {
+ Exception error = null;
+ try
+ {
+ if( server == null )
+ {
+ // Try getting the info from the client state
+ connect();
+ }
+ else
+ {
+ connect( server, port );
+ }
+ keeptrying = false;
+ }
+ catch( Exception e )
+ {
+ error = e;
+ keeptrying = true;
+ }
+
+ if( keeptrying )
+ {
+ keeptrying = failedToConnect( error );
+ }
+ }
+ }
+
+ /**
+ * Called when the final failure has occurred. Default implementation
+ * does nothing.
+ * */
+ protected void finalFailure()
+ {
+ //log.debug("AutoReconnect: Final failure.");
+ this.attempt = 0;
+ }
+
+ /**
+ * Called when a failure to connect occurs. This method should do
+ * whatever needs to be done between connection attempts; usually this
+ * will be sleeping the current thread and checking if we should stop
+ * trying to reconnect.
+ *
+ * @param error Exception that caused the failure
+ * @return true if another attempt at connecting should occur
+ * */
+ protected boolean failedToConnect( Exception error )
+ {
+ //log.debug("AutoReconnect: Error connecting: " + error);
+
+ this.attempt++;
+
+ // abort if we've tried too many times
+ if( attempt >= maxAttempts )
+ {
+ //log.debug("AutoReconnect: Tried " + attempt + " times, giving up.");
+ finalFailure();
+ return false;
+ }
+
+ // Well, try again damnit!
+ // Sleep for a bit first.
+ try
+ {
+ Thread.sleep( sleepTime );
+ }
+ catch( InterruptedException ie )
+ {
+ // And we're going to do what?
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * AutoReconnect will disable itself if it sees a quit command
+ * generated by returned from the server. This usually happens just
+ * before the server terminates the connection. Note that this would
+ * really be better if we could intercept messages on their way out,
+ * but Martyr doesn't do that.
+ */
+ protected void updateCommand( InCommand command )
+ {
+ if( disableOnQuit
+ && command instanceof QuitCommand
+ && ((QuitCommand)command).isOurQuit(getConnection().getClientState()) )
+ {
+ //log.debug("AutoReconnect: Disabling due to receiving own QUIT.");
+ disable();
+ }
+ }
+
+ public String toString()
+ {
+ return "AutoReconnect [" + attempt + "]";
+ }
+
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoRegister.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoRegister.java
new file mode 100644
index 000000000..f0cec08ba
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoRegister.java
@@ -0,0 +1,289 @@
+package f00f.net.irc.martyr.services;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.GenericAutoService;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.TimerTaskCommand;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.commands.NickCommand;
+import f00f.net.irc.martyr.commands.UserCommand;
+import f00f.net.irc.martyr.commands.PassCommand;
+import f00f.net.irc.martyr.errors.NickInUseError;
+import f00f.net.irc.martyr.util.FullNick;
+import java.util.logging.Logger;
+
+/**
+ * <p>AutoRegister performs the task of registering the user with the server
+ * once connected, including finding an appropriate nickname to use if the
+ * desired one is taken.</p>
+ *
+ * <p>AutoRegister's default behaviour is to send the provided nickname. If it
+ * receives an ERR_NICKNAMEINUSE while unregistered, AutoRegister will try
+ * again, with an _ appended to the nick. If this fails five times,
+ * AutoRegister will ask the IRCConnection to disconnect(). Note that if it
+ * fails to connect it remains enabled, so that if IRCConnection.connect() is
+ * called, it will re-try the same 5 NICKs.</p>
+ *
+ * <p>This default behaviour can be overridden by subclassing AutoRegister and
+ * overriding the getNickIterator( String baseNick ) method. It returns an
+ * instance of the java.util.Iterator interface which supplies nicknames (each
+ * object retreived from the Iterator is presumed to be a String).
+ * AutoRegister will iterate through the nickname list until there are no more
+ * items, at which point it will stop. For simple tasks such as providing a
+ * custom way to form new nicknames, overriding getNickIterator is
+ * sufficient.</p>
+ *
+ * <p>AutoRegister will add itself as a state observer and as a command
+ * observer. It needs to receive the error.NickInUseError command so that
+ * it can re-try the registration, and it needs to detect when we
+ * transition into the UNREGISTERED state.</p>
+ *
+ * <p>AutoRegister should be created before the IRCConnection.connect()
+ * is called. AutoRegister can be disabled by calling the 'disable()'
+ * method at any time. This simply removes AutoRegister as an
+ * observer for the state and commands.</p>
+ *
+ */
+public class AutoRegister extends GenericAutoService
+{
+ static Logger log = Logger.getLogger(AutoRegister.class.getName());
+
+ // I've lost track of why the timer stuff was in here. I think the
+ // original purpose was to make AutoRegister take control of the nick
+ // more *after* registration occurred. This code is now causing so
+ // many problems *before* registration, that I think it may need to be
+ // pulled out. Maybe time to bring a "Manager" service into the
+ // fold?
+ private long nickTimerTaskDelay = 10*1000;
+ private TimerTaskCommand nickTimerTask;
+
+ // Kept so it can be passed to getNickIterator()
+ private String originalNick;
+ // Used to set the client state once we register properly.
+ private String lastTryNick = null;
+ // Passed to the server on login
+ private String user;
+ private String name;
+ private String pass;
+ // Our list of nicks.
+ private Iterator nickIterator = null;
+ // attempt is only used for the debug output.
+ private int attempt = 0;
+
+ public static final int MAX_ATTEMPTS = 5;
+
+ public AutoRegister( IRCConnection connection, String nick,
+ String user, String name )
+ {
+ super( connection );
+
+ this.originalNick = nick;
+ this.user = user;
+ this.name = name;
+ this.pass = null;
+
+ enable();
+ }
+
+ public AutoRegister( IRCConnection connection, String nick,
+ String user, String name, String pass)
+ {
+ super( connection );
+
+ this.originalNick = nick;
+ this.user = user;
+ this.name = name;
+ this.pass = pass;
+
+ enable();
+ }
+
+
+ /**
+ * <p>This method supplies an Iterator that generates nicknames. Each successive
+ * failed attempt to login to the server with a nickname will be met with a new
+ * try using the next nickname in the iterator. When there are no more
+ * nicknames in the Iterator, AutoRegister gives up. Defining the Iterator as
+ * an anonymous class works well.</p>
+ *
+ * <p>The iterator should iterate over String objects.</p>
+ *
+ * @param baseNick The nickname passed into the constructor.
+ * @return Iterator over other attempts of nicks to try
+ */
+ protected Iterator getNickIterator( final String baseNick )
+ {
+ // This is simple and clean.. define the nick generation scheme as an
+ // anonymous class.
+ return new UnderscoreIterator(baseNick);
+ }
+
+ private static class UnderscoreIterator implements Iterator
+ {
+ int count = 1;
+ String nick;
+
+ public UnderscoreIterator( String base )
+ {
+ this.nick = base;
+ }
+
+ public boolean hasNext()
+ {
+ return count <= MAX_ATTEMPTS;
+ }
+
+ public Object next()
+ {
+ if( hasNext() )
+ {
+ String result = nick;
+
+ // Set up the next round
+ nick = nick + "_";
+ ++count;
+
+ // return the value for this round.
+ return result;
+ }
+ else
+ {
+ throw new NoSuchElementException("No more nicknames");
+ }
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ protected void updateState( State state )
+ {
+ //log.debug("AutoRegister: Update with state " + state);
+ if( state == State.UNREGISTERED )
+ {
+ // We need to do some registerin'!
+ nickIterator = getNickIterator( originalNick );
+ attempt = 0;
+ doRegister();
+ }
+ else if( state == State.REGISTERED )
+ {
+ // We need to update the client state.
+ ClientState clientState = getConnection().getClientState();
+ clientState.setNick( new FullNick( lastTryNick ) );
+ clientState.setName( name );
+ clientState.setUser( user );
+ clientState.setPass( pass );
+ }
+
+ //log.debug("AutoRegister: Returned from " + state);
+ }
+
+ protected void updateCommand( InCommand command )
+ {
+ // First, check the state, because if we are already registered
+ // then none of this matters.
+ //if( getConnection().getState() == State.REGISTERED )
+ //{
+ // // We're registered.
+ // // No reason to continue.
+ // return;
+ //}
+
+ if( command instanceof NickInUseError)
+ {
+ // If we get an error, then try another nick
+ NickInUseError nickErr = (NickInUseError)command;
+ if(nickErr.getNick().getNick().equals(originalNick))
+ {
+ cancelNickAttempt(); // We don't want more than one of these
+
+ scheduleNickAttempt();
+ }
+ if(getConnection().getState() == State.UNREGISTERED )
+ {
+ // re-register.
+ doRegister();
+ }
+ }
+ else if( command instanceof NickCommand )
+ {
+ // If we get a nick... then cancel a pending change
+ NickCommand nickCmd = (NickCommand)command;
+ if( nickCmd.getOldNick().equalsIgnoreCase( originalNick ) )
+ {
+ cancelNickAttempt();
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private void scheduleNickAttempt()
+ {
+ if( getConnection().getState().equals(State.REGISTERED))
+ {
+ // We're already connected.
+ // We're short-circuiting
+ return;
+ }
+ if(nickTimerTask == null || !nickTimerTask.isScheduled())
+ {
+ nickTimerTask = new TimerTaskCommand(getConnection(), new NickCommand(originalNick));
+ //TODO back off delay on repeated retries?
+ getConnection().getCronManager().schedule(nickTimerTask, nickTimerTaskDelay);
+ }
+ }
+
+ private void cancelNickAttempt()
+ {
+ if(nickTimerTask != null && nickTimerTask.isScheduled())
+ {
+ nickTimerTask.cancel();
+ }
+ }
+
+ private void doRegister()
+ {
+ if( getConnection().getState() != State.UNREGISTERED )
+ {
+ log.severe("AutoRegister: Tried to register but we are not UNREGISTERED");
+ return;
+ }
+
+ if( ! nickIterator.hasNext() )
+ {
+ log.info("AutoRegister: Failed to register.");
+ getConnection().disconnect();
+ return;
+ }
+
+ lastTryNick = (String)nickIterator.next();
+ ++attempt;
+ log.info("AutoRegister: Trying to register as " + lastTryNick);
+
+ if (pass != null) {
+ getConnection().sendCommand( new PassCommand( pass ));
+ }
+ getConnection().sendCommand( new NickCommand( lastTryNick ) );
+ getConnection().sendCommand( new UserCommand( user, name, getConnection() ) );
+ }
+
+ public String toString()
+ {
+ return "AutoRegister [" + attempt + "]";
+ }
+
+ // END AutoRegister
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoResponder.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoResponder.java
new file mode 100644
index 000000000..08f3a7f29
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoResponder.java
@@ -0,0 +1,82 @@
+package f00f.net.irc.martyr.services;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.commands.ChannelModeCommand;
+import f00f.net.irc.martyr.commands.JoinCommand;
+import f00f.net.irc.martyr.commands.PingCommand;
+import f00f.net.irc.martyr.commands.PongCommand;
+
+/**
+ * AutoResponder is where commands that should be auto-responded (such
+ * as PING-PONG) should go.
+ */
+public class AutoResponder implements Observer
+{
+
+ private IRCConnection connection;
+ private boolean enabled = false;
+
+ public AutoResponder( IRCConnection connection )
+ {
+ this.connection = connection;
+ enable();
+ }
+
+ public void enable()
+ {
+ if( enabled )
+ return;
+
+ connection.addCommandObserver( this );
+ enabled = true;
+ }
+
+ public void disable()
+ {
+ if( !enabled )
+ return;
+
+ connection.removeCommandObserver( this );
+ enabled = false;
+ }
+
+ /**
+ * Does the work of figuring out what to respond to.
+ * If a PING is received, send a PONG. If we JOIN a channel, send a
+ * request for modes.
+ * */
+ public void update( Observable observer, Object updated )
+ {
+
+ if( updated instanceof PingCommand )
+ {
+ // We need to do some pongin'!
+ PingCommand ping = (PingCommand)updated;
+
+ String response = ping.getPingSource();
+
+ connection.sendCommand( new PongCommand( response ) );
+ }
+ else if( updated instanceof JoinCommand )
+ {
+ // Determine if we joined, and if we did, trigger a MODE discovery
+ // request.
+ JoinCommand join = (JoinCommand)updated;
+
+ if( join.weJoined( connection.getClientState() ) )
+ {
+ connection.sendCommand(
+ new ChannelModeCommand( join.getChannel() ) );
+ }
+ }
+ }
+
+ // END AutoResponder
+}
+
+
+
+