--- projects/cms/source/server/uk/org/iscream/cms/server/client/alerters/IRC__Alerter.java 2002/01/17 17:58:12 1.30 +++ projects/cms/source/server/uk/org/iscream/cms/server/client/alerters/IRC__Alerter.java 2002/02/07 17:15:19 1.31 @@ -10,6 +10,7 @@ import java.io.*; import java.net.*; import java.util.*; import java.text.DateFormat; +import org.jibble.pircbot.*; /** * This Alert sends an IRC message. @@ -18,7 +19,7 @@ import java.text.DateFormat; * IRCBot inner class. * * @author $Author: tdb $ - * @version $Id: IRC__Alerter.java,v 1.30 2002/01/17 17:58:12 tdb Exp $ + * @version $Id: IRC__Alerter.java,v 1.31 2002/02/07 17:15:19 tdb Exp $ */ public class IRC__Alerter extends AlerterSkeleton { @@ -27,29 +28,27 @@ public class IRC__Alerter extends AlerterSkeleton { /** * The current CVS revision of this class */ - public final String REVISION = "$Revision: 1.30 $"; + public final String REVISION = "$Revision: 1.31 $"; /** * A description of this alerter */ public final String DESC = "Sends alerts on an IRC channel"; - /** - * The default reconnect delay in seconds - */ - public final int DEFAULT_RECONNECT_DELAY = 30; - //---STATIC METHODS--- //---CONSTRUCTORS--- - + public IRC__Alerter() { super(); - // connect to the IRC server + // construct and initialise the bot _ircbot = new IRCBot(); + _ircbot.setVerbose(false); + Thread ircThread = new Thread(_ircbot); // set it's name and start it - _ircbot.setName("client.IRC__Alerter$IRCBot"); - _ircbot.start(); + ircThread.setName("client.IRC__Alerter$IRCBot"); + ircThread.start(); + // log our start time _startTime = System.currentTimeMillis(); _logger.write(toString(), Logger.SYSINIT, "IRC Alerter started"); } @@ -78,7 +77,7 @@ public class IRC__Alerter extends AlerterSkeleton { if(_active) { // send the message _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level"); - _ircbot.sendMsg(message); + _ircbot.sendMessage(message); // count sent alerts _alertCount++; } else { @@ -169,6 +168,12 @@ public class IRC__Alerter extends AlerterSkeleton { private long _startTime; /** + * This holds a reference to the + * system logger that is being used. + */ + protected Logger _logger = ReferenceManager.getInstance().getLogger(); + + /** * This is the friendly identifier of the * component this class is running in. * eg, a Filter may be called "filter1", @@ -183,144 +188,49 @@ public class IRC__Alerter extends AlerterSkeleton { //---INNER CLASSES--- - /** - * This class provides some basic IRCBot functionality. It connects - * to a specified server, and will remain there until told to - * leave. Whilst connected it can send a message or a notice to - * the server. - */ - class IRCBot extends Thread { + class IRCBot extends PircBot implements Runnable { - public static final String DEFAULT_STARTUP_NOTICE = "i-scream ircbot starting..."; + /** + * The default reconnect delay in seconds + */ + public final int DEFAULT_RECONNECT_DELAY = 30; /** - * Main thread loop, this part of the class listens for - * messages from the server, and acts accordingly. At the - * present moment it only responds to pings. + * Attempt to kick-start the IRCBot into action. Will + * keep calling init() until it doesn't throw an + * an IOException (which will only be thrown if there + * is an error initialising). After a successful call + * to init() the method finishes. */ public void run() { - // so we can stop if required - boolean run = true; - while(run) { - // flag so we can stop the loop - boolean doRead = true; - // get the startup notice - String startupNotice; + while(true) { try { - startupNotice = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice"); - } catch (PropertyNotFoundException e) { - startupNotice = DEFAULT_STARTUP_NOTICE; - _logger.write(this.toString(), Logger.WARNING, "Configuration error: "+e); + init(); + break; } - // connect to the IRC server - try { - connect(); - sendNotice(startupNotice); - } catch(IOException e) { - doRead=false; - _logger.write(this.toString(), Logger.ERROR, "Error connecting: "+e); + catch (IOException e) { + _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e); + // wait for a while, defined in the config + reconnectSleep(); } - while(doRead) { - try { - // read a command - String cmd = _socketIn.readLine(); - // if we have a null, we've lost contact - if(cmd == null) { - throw new IOException("End of stream reached"); - } - // let another method deal with the input - handleInput(cmd); - } catch (IOException e) { - // comms failure, maybe our link is dead. - _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e); - // stop, and loop round for a reconnect. - doRead = false; - } - } - // make sure we're disconnected - try { - disconnect(); - } catch (IOException e) { - _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e); - } - - // comms have failed, so wait a while and reconnect - int delayTime = 0; - try { - delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay")); - } catch (NumberFormatException e) { - delayTime = DEFAULT_RECONNECT_DELAY; - _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds"); - } catch (PropertyNotFoundException e) { - delayTime = DEFAULT_RECONNECT_DELAY; - _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds"); - } - try { - Thread.sleep(delayTime * 1000); - } catch (InterruptedException e) {} } - // maybe disconnect here ? - shutdown method not implemented yet - //disconnect(); } /** - * Sends a message to the channel. + * Connects the bot to an irc server and joins a channel, + * using details supplied from the i-scream configuration + * system. If this method completes without an exception + * one can presume the bot is ready for use. * - * @param msg The message to send + * @throws IOException if there is any problem initialising */ - public void sendMsg(String msg) { - _socketOut.println("PRIVMSG "+_channel+" :"+msg); - // wait a second before returning... - // this ensures messages can't be sent too fast - try {Thread.sleep(1000);} catch (InterruptedException e) {} - } - - /** - * Sends a message to the channel. - * - * @param user The user to send to - * @param msg The message to send - */ - public void sendPrivMsg(String user, String msg) { - _socketOut.println("PRIVMSG "+user+" :"+msg); - // wait a second before returning... - // this ensures messages can't be sent too fast - try {Thread.sleep(1000);} catch (InterruptedException e) {} - } - - /** - * Sends an action to the channel. - * - * @param msg the action message - */ - public void sendAction(String msg) { - char esc = 001; - sendMsg(esc+"ACTION "+msg+esc); - // wait a second before returning... - // this ensures messages can't be sent too fast - try {Thread.sleep(1000);} catch (InterruptedException e) {} - } - - /** - * Sends a notice to the channel. - * - * @param msg The notice to send - */ - public void sendNotice(String msg) { - _socketOut.println("NOTICE "+_channel+" :"+msg); - // wait a second before returning... - // this ensures messages can't be sent too fast - try {Thread.sleep(1000);} catch (InterruptedException e) {} - } - - /** - * Connect to the IRC server, log in, and join the channel. - * - * @throws IOException if the connection fails - */ - public void connect() throws IOException { + private void init() throws IOException { + _logger.write(this.toString(), Logger.DEBUG, "Initialising IRCBot..."); + + // get a hook on the configuration system ConfigurationProxy cp = ConfigurationProxy.getInstance(); - // setup the socket, reader and writer + + // get hold of the server details String server; int port; try { @@ -333,304 +243,415 @@ public class IRC__Alerter extends AlerterSkeleton { _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e); throw new IOException("Can't get irc server details due to malformed configuration"); } - _socket = new Socket(server, port); - _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream())); - _socketOut = new PrintWriter(_socket.getOutputStream(), true); - //_socketOut.println("PASS"); - // send USER details - String user, comment; + + // get hold of the user details and nickname list + String user, nickList; try { user = cp.getProperty(_name, "Alerter.IRC.user"); - comment = cp.getProperty(_name, "Alerter.IRC.comment"); + nickList = cp.getProperty(_name, "Alerter.IRC.nickList"); } catch (PropertyNotFoundException e) { _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e); - throw new IOException("Can't get user details due to configuration error"); + throw new IOException("Can't get user/nickname details due to configuration error"); } - _socketOut.println("USER "+user+" 8 * :"+comment); - // attempt to get a nick - String nickList; + + // get hold of comment and finger information + // -- we're not too fussed if these aren't set + String comment = "Alerter.IRC.comment is undefined"; + String finger = "Alerter.IRC.finger is undefined"; try { - nickList = cp.getProperty(_name, "Alerter.IRC.nickList"); + comment = cp.getProperty(_name, "Alerter.IRC.comment"); + finger = cp.getProperty(_name, "Alerter.IRC.finger"); } catch (PropertyNotFoundException e) { - _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e); - throw new IOException("Can't get nickname due to configuration error"); + _logger.write(this.toString(), Logger.WARNING, "Configuration warning, using default: "+e); } + + // put these details into the bot + this.setLogin(user); + this.setVersion("[" + getRevision() + "] " + comment); + this.setFinger(finger); + + // attempt to connect, trying each nickname + // in turn until sucessfully connected StringTokenizer st = new StringTokenizer(nickList, ";"); boolean ok = false; - // try until we exhaust our list while(!ok && st.hasMoreTokens()) { String nick = st.nextToken(); - _socketOut.println("NICK "+nick); - // get a "yes" or "no" response back - String response = ""; - do { - response = _socketIn.readLine(); - if(response==null) { - throw new IOException("Communication error whilst logging in"); - } - } while(response.indexOf("001")==-1 && response.indexOf("433")==-1); - // see if it was a yes - if(response.indexOf("001")!=-1) { - // great, we're logged in ! - ok = true; - // store the name we're using + try { + // try to connect with a nickname + _logger.write(this.toString(), Logger.DEBUG, "Trying nick: "+nick); + this.setName(nick); + this.connect(server, port); + // at this point we know the nickname was accepted _nickname = nick; + ok = true; } - else { - // log we couldn't get the name - _logger.write(this.toString(), Logger.WARNING, "Nickname in use: "+nick); + catch(IOException e) { + _logger.write(this.toString(), Logger.ERROR, "IO error when connecting to server: "+e); + throw new IOException("IO error when connecting to server"); } + catch(IrcException e) { + _logger.write(this.toString(), Logger.ERROR, "IRC error when connecting to server: "+e); + throw new IOException("IRC error when connecting to server"); + } + catch(NickAlreadyInUseException e) { + _logger.write(this.toString(), Logger.ERROR, "Nickname "+nick+" is already in use: "+e); + // don't do anything, instead just loop round + // and try the next nickname in the list + } } if(!ok) { - // oh dear, we couldn't get on. - throw new IOException("All nicknames in use"); + // must have tried all the nicknames, best bail out + _logger.write(this.toString(), Logger.ERROR, "All nicknames already in use"); + throw new IOException("All nicknames already in use"); } - // join the channel + + // get hold of the channel details, and attempt + // to join the specified channel try { _channel = cp.getProperty(_name, "Alerter.IRC.channel"); + this.joinChannel(_channel); } catch (PropertyNotFoundException e) { _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e); throw new IOException("Can't get channel name due to configuration error"); } - _socketOut.println("JOIN "+_channel); - // allow alerts + + // get hold of the startup notice, and send + // it if it's defined + String startupNotice; + try { + startupNotice = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice"); + sendNotice(_channel, startupNotice); + } catch (PropertyNotFoundException e) { + _logger.write(this.toString(), Logger.DEBUG, "Startup notice not defined, so not sending: "+e); + } + + // at this point initialisation is complete, so + // we can return and set this flag to allow alerts _active = true; } /** - * Disconnect "nicely" from the IRC server. + * Send a message to the current channel. * - * @throws IOException if the disconnection fails + * @param message The message to send */ - public void disconnect() throws IOException { - // stop alerts + private void sendMessage(String message) { + sendMessage(_channel, message); + } + + /** + * If we get disconnected this method will be + * called, so we must take action. We'll simply + * keep trying to reinitialise, and thus + * reconnect to the server. + */ + public void onDisconnect() { + // stop alerts being sent for now _active = false; - // send proper QUIT - _socketOut.println("QUIT : iscreamBot component shutting down..."); - // close the socket - _socketOut.close(); - _socketIn.close(); - _socket.close(); + while(true) { + // wait for a while, defined in the config + reconnectSleep(); + try { + init(); + break; + } + catch (IOException e) { + _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e); + } + } } /** - * Overrides the {@link java.lang.Object#toString() Object.toString()} - * method to provide clean logging (every class should have this). + * If we receive a message this method will + * be called. We'll check if the message is + * for us, and call handleInput if it is. * - * This uses the uk.org.iscream.cms.server.util.NameFormat class - * to format the toString() + * @param channel The channel the message came from + * @param sender The sender of the message + * @param login The login of the sender + * @param hostname The hostname of the sender + * @param message The message sent + */ + public void onMessage(String channel, String sender, String login, String hostname, String message) { + String trimmedMessage = isForMe(message); + // if trimmedMessage is null, it's not for us + if(trimmedMessage != null) { + handleInput(trimmedMessage, channel); + } + } + + /** + * If we receive a private message this method + * will be called. No need to check if it's for + * us -- it has to be if it's a private message. * - * @return the name of this class and its CVS revision + * @param sender The sender of the message + * @param login The login of the sender + * @param hostname The hostname of the sender + * @param message The message sent */ - public String toString() { - return FormatName.getName( - _name, - getClass().getName(), - REVISION); + public void onPrivateMessage(String sender, String login, String hostname, String message) { + handleInput(message, sender); } /** - * Deals with incoming lines from the server. + * If we receive a nick change message, this + * method gets called. We don't care about + * other users changing their nick, so we only + * take notice if our nick changes. If it does + * we need to change our internal nickname + * state. * - * @param line the line to deal with + * @param oldNick the old nickname + * @param login the login of the nick changer + * @param hostname the hostname of the nick changer + * @param newNick the new nickname */ - private void handleInput(String line) { - ConfigurationProxy cp = ConfigurationProxy.getInstance(); - // if it's a PING... - if(line.startsWith("PING")) { - // ...send a PONG - _socketOut.println("PONG" + line.substring(4)); + public void onNickChange(String oldNick, String login, String hostname, String newNick) { + if(oldNick.equals(_nickname)) { + _nickname = newNick; + // tell the underlying pircbot that our nick has changed too :) + setName(newNick); } - // see if it's for us - else if(getMsg(line).toLowerCase().startsWith(_nickname.toLowerCase()+",") || - getMsg(line).toLowerCase().startsWith(_nickname.toLowerCase()+":") || - getMsg(line).toLowerCase().startsWith(_nickname.toLowerCase()+" ")) { - // setup some String's - String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand; - String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand; - // get the command set + } + + /** + * If we receive a kick message this method + * gets called. We only take notice if it's + * us getting kicked, and then we'll rejoin + * the channel after a short wait. + * + * @param channel the channel the kick happened on + * @param kickerNick the person who performed the kick + * @param kickerLogin the login of the person who performed the kick + * @param kickerHostname the hostname of the person who performed the kick + * @param recipientNick the nickname of the person being kicked + * @param reason the reason for the kick + */ + public void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason) { + if(recipientNick.equals(_nickname) && channel.equals(_channel)) { + // remind the person it's not very nice :) + sendMessage(kickerNick, "That wasn't a nice thing to do..."); + // get hold of the channel details, in case they've changed try { - stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand"); - startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand"); - timeSinceLastAlertCommand = cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand"); - lastAlertCommand = cp.getProperty(_name, "Alerter.IRC.lastAlertCommand"); - joinCommand = cp.getProperty(_name, "Alerter.IRC.joinCommand"); - nickChangeCommand = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand"); - versionCommand = cp.getProperty(_name, "Alerter.IRC.versionCommand"); - helpCommand = cp.getProperty(_name, "Alerter.IRC.helpCommand"); - statCommand = cp.getProperty(_name, "Alerter.IRC.statCommand"); - uptimeCommand = cp.getProperty(_name, "Alerter.IRC.uptimeCommand"); - } catch (PropertyNotFoundException e) { - _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e); - // lets bail from handling this line... - // ...it's gonna be hard without a command set! - return; + _channel = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.channel"); + } catch(PropertyNotFoundException e) { + _logger.write(this.toString(), Logger.ERROR, "Can't get channel name due to configuration error: "+e); } - - // we have a message for us - String message = getMsg(line).substring(_nickname.length()); - if(message.indexOf(stopCommand)!=-1) { - _active = false; - sendMsg(getMsgSender(line)+", alerts have been stopped"); + // wait for a while, defined in the config + reconnectSleep(); + // we'll try and rejoin the channel regardless + // otherwise we might end up doing nothing! + joinChannel(_channel); + } + } + + /** + * This method handles input directed to us, and + * responds accordingly if required. + * + * @param message the message from someone + * @param source where the message came from, so we know where to send the response + */ + private void handleInput(String message, String source) { + // get hold of the configuration system + ConfigurationProxy cp = ConfigurationProxy.getInstance(); + // setup some String's + String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand; + String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand; + // get the command set from the configuration + try { + stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand"); + startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand"); + timeSinceLastAlertCommand = cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand"); + lastAlertCommand = cp.getProperty(_name, "Alerter.IRC.lastAlertCommand"); + joinCommand = cp.getProperty(_name, "Alerter.IRC.joinCommand"); + nickChangeCommand = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand"); + versionCommand = cp.getProperty(_name, "Alerter.IRC.versionCommand"); + helpCommand = cp.getProperty(_name, "Alerter.IRC.helpCommand"); + statCommand = cp.getProperty(_name, "Alerter.IRC.statCommand"); + uptimeCommand = cp.getProperty(_name, "Alerter.IRC.uptimeCommand"); + } catch (PropertyNotFoundException e) { + _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e); + // lets bail from handling this line... + // ...it's gonna be hard without a command set! + return; + } + + if(message.equalsIgnoreCase(stopCommand)) { + _active = false; + sendMessage(source, "alerts have been stopped"); + } + else if(message.equalsIgnoreCase(startCommand)) { + _active = true; + sendMessage(source, "alerts have been activated"); + } + // this needs to go before lastAlertCommand if it contains + // the same words as the lastAlertCommand. + else if(message.equalsIgnoreCase(timeSinceLastAlertCommand)) { + if(_lastAlertTime != -1) { + long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000; + String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs"); + sendMessage(source, "I last sent an alert "+uptimeText+ " ago"); } - else if(message.indexOf(startCommand)!=-1) { - _active = true; - sendMsg(getMsgSender(line)+", alerts have been activated"); + else { + sendMessage(source, "I've never sent an alert!"); } - // this needs to go here if it contains the same words as the lastAlertCommand - else if(message.indexOf(timeSinceLastAlertCommand)!=-1) { - if(_lastAlertTime != -1) { - long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000; - String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs"); - sendMsg(getMsgSender(line)+", I last sent an alert "+uptimeText+ " ago"); - } - else { - sendMsg(getMsgSender(line)+", I've never sent an alert!"); - } + } + else if(message.equalsIgnoreCase(lastAlertCommand)) { + if(_lastAlertTime != -1) { + String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime)); + sendMessage(source, "last alert was at "+date+"; "+_lastAlert); } - else if(message.indexOf(lastAlertCommand)!=-1) { - if(_lastAlertTime != -1) { - String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime)); - sendMsg(getMsgSender(line)+", last alert was at "+date+"; "+_lastAlert); - } - else { - sendMsg(getMsgSender(line)+", I've never sent an alert!"); - } - + else { + sendMessage(source, "I've never sent an alert!"); } - else if(message.indexOf(joinCommand)!=-1) { - String joinCmd = joinCommand; - String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1); - int endOfChan = newChan.indexOf(" "); - if(endOfChan == -1) { - endOfChan = newChan.length(); - } - newChan = newChan.substring(0, endOfChan); - if(newChan.equals(_channel)) { - sendMsg(getMsgSender(line)+", I'm already on "+newChan+"!"); - } else { - sendMsg(getMsgSender(line)+", okay, I'm off to "+newChan); - _socketOut.println("PART "+_channel); - _socketOut.println("JOIN "+newChan); - _channel = newChan; - } + + } + else if(message.toLowerCase().startsWith(joinCommand.toLowerCase())) { + String joinCmd = joinCommand; + String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1); + int endOfChan = newChan.indexOf(" "); + if(endOfChan == -1) { + endOfChan = newChan.length(); } - else if(message.indexOf(nickChangeCommand)!=-1) { - String nickChangeCmd = nickChangeCommand; - String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1); - int endOfNick = newNick.indexOf(" "); - if(endOfNick == -1) { - endOfNick = newNick.length(); - } - newNick = newNick.substring(0, endOfNick); - sendMsg(getMsgSender(line)+", okay, changing my nickname to "+newNick); - _socketOut.println("NICK "+newNick); - _nickname = newNick; + newChan = newChan.substring(0, endOfChan); + if(newChan.equals(_channel)) { + sendMessage(source, "I'm already on "+newChan+"!"); + } else { + partChannel(_channel); + joinChannel(newChan); + _channel = newChan; } - else if(message.indexOf(versionCommand)!=-1) { - sendMsg(getMsgSender(line)+", I am version "+REVISION.substring(11, REVISION.length() -2)+" of the i-scream alerting bot"); + } + else if(message.toLowerCase().startsWith(nickChangeCommand.toLowerCase())) { + String nickChangeCmd = nickChangeCommand; + String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1); + int endOfNick = newNick.indexOf(" "); + if(endOfNick == -1) { + endOfNick = newNick.length(); } - else if(message.indexOf(helpCommand)!=-1) { - sendPrivMsg(getMsgSender(line), "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2)); - sendPrivMsg(getMsgSender(line), "I understand the following commands;"); - sendPrivMsg(getMsgSender(line), stopCommand); - sendPrivMsg(getMsgSender(line), startCommand); - sendPrivMsg(getMsgSender(line), lastAlertCommand); - sendPrivMsg(getMsgSender(line), joinCommand); - sendPrivMsg(getMsgSender(line), nickChangeCommand); - sendPrivMsg(getMsgSender(line), statCommand); - sendPrivMsg(getMsgSender(line), uptimeCommand); - sendPrivMsg(getMsgSender(line), timeSinceLastAlertCommand); - sendPrivMsg(getMsgSender(line), helpCommand); - } - else if(message.indexOf(statCommand)!=-1) { - sendMsg(getMsgSender(line)+", I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!"); - } - else if(message.indexOf(uptimeCommand)!=-1) { - long uptime = (System.currentTimeMillis() - _startTime) / 1000; - String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs"); - sendMsg(getMsgSender(line)+", I have been running for "+uptimeText); - } - else if(message.indexOf("ping")!=-1) { - sendMsg("pong"); - } - else if(message.indexOf("do a jibble dance")!=-1) { - // little joke :) - sendAction("jives to the funky beat shouting \"ii--screeeaaammm\""); - } - else { - String rejectMessage = NOT_CONFIGURED; - try { - rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage"); - } catch(PropertyNotFoundException e) { - _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e); - } - sendMsg(getMsgSender(line)+", "+rejectMessage); - } + newNick = newNick.substring(0, endOfNick); + changeNick(newNick); } - else if(line.indexOf(_nickname)!=-1 && line.indexOf(_channel)!=-1 && line.indexOf("KICK")!=-1) { - sendPrivMsg(getMsgSender(line), "That wasn't a nice thing to do..."); + else if(message.equalsIgnoreCase(versionCommand)) { + sendMessage(source, "I am version " + getRevision() + " of the i-scream alerting bot"); + } + else if(message.equalsIgnoreCase(helpCommand)) { + sendMessage(source, "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2)); + sendMessage(source, "I understand the following commands;"); + sendMessage(source, stopCommand); + sendMessage(source, startCommand); + sendMessage(source, lastAlertCommand); + sendMessage(source, joinCommand); + sendMessage(source, nickChangeCommand); + sendMessage(source, statCommand); + sendMessage(source, uptimeCommand); + sendMessage(source, timeSinceLastAlertCommand); + sendMessage(source, helpCommand); + } + else if(message.equalsIgnoreCase(statCommand)) { + sendMessage(source, "I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!"); + } + else if(message.equalsIgnoreCase(uptimeCommand)) { + long uptime = (System.currentTimeMillis() - _startTime) / 1000; + String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs"); + sendMessage(source, "I have been running for "+uptimeText); + } + else if(message.equalsIgnoreCase("ping")) { + sendMessage(source, "pong"); + } + else if(message.equalsIgnoreCase("do a jibble dance")) { + // little joke :) + sendAction(source, "jives to the funky beat shouting \"ii--screeeaaammm\""); + } + else { + String rejectMessage = NOT_CONFIGURED; try { - _channel = cp.getProperty(_name, "Alerter.IRC.channel"); + rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage"); } catch(PropertyNotFoundException e) { _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e); } - _socketOut.println("JOIN "+_channel); + sendMessage(source, rejectMessage); } } /** - * Strips the header from a message line + * Quick method to check if the message appears + * to be directed at us. We simply check to see + * if it starts with our nick in some fashion. + * This will return null if the message is not + * for us - thus allowing this method to perform + * two tasks at once. * - * @param line the line to strip - * @return the message from the line + * @param message the message to check + * @return the message (with our nick removed), or null if it's not for us. */ - private String getMsg(String line) { - String result = ""; - if(line.indexOf("PRIVMSG")!=-1) { - int firstColon = line.indexOf(":"); - if(firstColon != -1) { - int secondColon = line.indexOf(":", firstColon+1); - if(secondColon != -1) { - result = line.substring(secondColon+1); - } - } + private String isForMe(String message) { + // change to lower case to remove + // case ambiguities + String nick = _nickname.toLowerCase(); + String msg = message.toLowerCase(); + if(msg.startsWith(nick + ", ") || + msg.startsWith(nick + ": ") || + msg.startsWith(nick + " ")) { + // NOTE that the 1 here is hardcoded to be the length + // of the above 'extra' character (eg , or :) + return message.substring(nick.length()+1).trim(); } - return result; + else { + return null; + } } /** - * Finds out the sender of the message - * - * @param line the line to look for a sender in - * @return the sender + * Sleep for a configurable amount of time and + * then return. This is used when reconnecting + * to the server or a channel. */ - private String getMsgSender(String line) { - String result = ""; - int colon = line.indexOf(":"); - int excl = line.indexOf("!"); - if(colon!=-1 && excl!=-1) { - result = line.substring(colon+1, excl); + private void reconnectSleep() { + int delayTime = 0; + try { + delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay")); + } catch (NumberFormatException e) { + delayTime = DEFAULT_RECONNECT_DELAY; + _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds"); + } catch (PropertyNotFoundException e) { + delayTime = DEFAULT_RECONNECT_DELAY; + _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds"); } - return result; + _logger.write(this.toString(), Logger.ERROR, "Waiting "+delayTime+" seconds for reconnect..."); + try { + Thread.sleep(delayTime * 1000); + } catch (InterruptedException e) {} } /** - * The socket connected to the server + * Returns the revision of the bot. + * + * @return the revision of this bot */ - private Socket _socket; + private String getRevision() { + return REVISION.substring(11, REVISION.length()-2); + } /** - * The writer + * Overrides the {@link java.lang.Object#toString() Object.toString()} + * method to provide clean logging (every class should have this). + * + * This uses the uk.org.iscream.cms.server.util.NameFormat class + * to format the toString() + * + * @return the name of this class and its CVS revision */ - private PrintWriter _socketOut; + public String toString() { + return FormatName.getName( + _name, + getClass().getName(), + REVISION); + } /** - * The reader - */ - private BufferedReader _socketIn; - - /** * Just a reminder to what channel we're on... * this can't be dynamic :) */ @@ -640,12 +661,6 @@ public class IRC__Alerter extends AlerterSkeleton { * A reminder of our current nickname... */ private String _nickname; - - /** - * This holds a reference to the - * system logger that is being used. - */ - protected Logger _logger = ReferenceManager.getInstance().getLogger(); }