ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/server/uk/org/iscream/cms/server/client/alerters/IRC__Alerter.java
(Generate patch)

Comparing projects/cms/source/server/uk/org/iscream/cms/server/client/alerters/IRC__Alerter.java (file contents):
Revision 1.7 by tdb, Sat Mar 3 01:09:53 2001 UTC vs.
Revision 1.31 by tdb, Thu Feb 7 17:15:19 2002 UTC

# Line 1 | Line 1
1   //---PACKAGE DECLARATION---
2 < package uk.ac.ukc.iscream.client.alerters;
2 > package uk.org.iscream.cms.server.client.alerters;
3  
4   //---IMPORTS---
5 < import uk.ac.ukc.iscream.client.*;
6 < import uk.ac.ukc.iscream.core.*;
7 < import uk.ac.ukc.iscream.util.*;
8 < import uk.ac.ukc.iscream.componentmanager.*;
9 <
5 > import uk.org.iscream.cms.server.client.*;
6 > import uk.org.iscream.cms.server.core.*;
7 > import uk.org.iscream.cms.server.util.*;
8 > import uk.org.iscream.cms.server.componentmanager.*;
9   import java.io.*;
10   import java.net.*;
11 + import java.util.*;
12 + import java.text.DateFormat;
13 + import org.jibble.pircbot.*;
14  
15   /**
16   * This Alert sends an IRC message.
17   *
18 + * Clean shutdown could be achieved by stopping the run() method in the
19 + * IRCBot inner class.
20 + *
21   * @author  $Author$
22   * @version $Id$
23   */
24 < public class IRC__Alerter implements PluginAlerter {
24 > public class IRC__Alerter extends AlerterSkeleton {
25  
26   //---FINAL ATTRIBUTES---
27  
# Line 25 | Line 30 | public class IRC__Alerter implements PluginAlerter {
30       */
31      public final String REVISION = "$Revision$";
32      
33 +    /**
34 +     * A description of this alerter
35 +     */
36      public final String DESC = "Sends alerts on an IRC channel";
37      
38   //---STATIC METHODS---
39  
40   //---CONSTRUCTORS---
41 <
41 >    
42      public IRC__Alerter() {
43 <                        
44 <        // connect to the IRC server
45 <        _ircbot = null;
46 <        try {
47 <            _ircbot = new IRCBot();
48 <            _ircbot.connect();
49 <            _ircbot.sendNotice("i-scream alerting bot activated");
50 <        } catch(IOException e) {
51 <            _logger.write(toString(), Logger.ERROR, "Error starting IRCBot: "+e);
52 <        }
45 <        
43 >        super();
44 >        // construct and initialise the bot
45 >        _ircbot = new IRCBot();
46 >        _ircbot.setVerbose(false);
47 >        Thread ircThread = new Thread(_ircbot);
48 >        // set it's name and start it
49 >        ircThread.setName("client.IRC__Alerter$IRCBot");
50 >        ircThread.start();
51 >        // log our start time
52 >        _startTime = System.currentTimeMillis();
53          _logger.write(toString(), Logger.SYSINIT, "IRC Alerter started");
54      }
55  
56   //---PUBLIC METHODS---
57  
58 +    /**
59 +     * Implements the abstract method from the skeleton class.
60 +     * This method will attempt to send an alert
61 +     * message over the IRC channel.
62 +     *
63 +     * @param alert the alert to send
64 +     */
65      public void sendAlert(Alert alert) {
66 <        ConfigurationProxy cp = ConfigurationProxy.getInstance();
67 <        int level = Integer.parseInt(cp.getProperty(_name, "Alerter.IRC.level"));
68 <        // only send if it's equal (or above) our level
69 <        if(alert.getLevel() >= level) {
70 <            String alertType = Alert.alerts[alert.getLevel()]+"["+String.valueOf(alert.getLevel())+"]";
71 <            // sort out the message
72 <            String message = cp.getProperty(_name, "Alerter.IRC.message");
73 <            message = StringUtils.replaceText(message, "%level%", alertType);
74 <            message = StringUtils.replaceText(message, "%source%", alert.getSource());
75 <            message = StringUtils.replaceText(message, "%value%", alert.getValue());
76 <            message = StringUtils.replaceText(message, "%thresholdValue%", alert.getThresholdValue());
77 <            message = StringUtils.replaceText(message, "%attributeName%", alert.getAttributeName());
64 <            
66 >        // sort out the message      
67 >        String alertType = Alert.alertLevels[alert.getLevel()];        
68 >        String message;
69 >        try {
70 >            message = _cp.getProperty(_name, "Alerter.IRC.message");
71 >        } catch (PropertyNotFoundException e) {
72 >            message = NOT_CONFIGURED;
73 >            _logger.write(toString(), Logger.WARNING, "Alerter.IRC.message value unavailable using default of " + message);
74 >        }
75 >        message = processAlertMessage(message, alert);        
76 >        // only send alerts if we're active
77 >        if(_active) {          
78              // send the message
79 <            _logger.write(toString(), Logger.DEBUG, "Sending IRC Alert at level"+String.valueOf(alert.getLevel()));
80 <            _ircbot.sendMsg(message);
79 >            _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level");
80 >            _ircbot.sendMessage(message);
81 >            // count sent alerts
82 >            _alertCount++;
83 >        } else {
84 >            // don't send, but keep a count that we ignored it
85 >            _ignoredCount++;
86          }
87 +        // we'll always store the "last alert", regardless
88 +        // of whether we actually display it or not
89 +        _lastAlert = message;
90 +        _lastAlertTime = System.currentTimeMillis();
91      }
92  
93      /**
94       * Overrides the {@link java.lang.Object#toString() Object.toString()}
95       * method to provide clean logging (every class should have this).
96       *
97 <     * This uses the uk.ac.ukc.iscream.util.NameFormat class
97 >     * This uses the uk.org.iscream.cms.server.util.NameFormat class
98       * to format the toString()
99       *
100       * @return the name of this class and its CVS revision
# Line 84 | Line 106 | public class IRC__Alerter implements PluginAlerter {
106              REVISION);
107      }
108  
109 <    /**
110 <     * return the String representation of what the filter does
109 >    /**
110 >     * Return the String representation of what the alerter does
111 >     *
112 >     * @return the description
113       */
114      public String getDescription(){
115          return DESC;
# Line 95 | Line 119 | public class IRC__Alerter implements PluginAlerter {
119  
120   //---ACCESSOR/MUTATOR METHODS---
121  
122 +    /**
123 +     * Returns the "friendly" name of this class. This
124 +     * is simply an accessor for _name, required due to
125 +     * inheritance issues with extending AlerterSkeleton.
126 +     *
127 +     * @return the friendly name
128 +     */
129 +    protected String getFName() {
130 +        return _name;
131 +    }
132 +
133   //---ATTRIBUTES---
134      
135      /**
# Line 103 | Line 138 | public class IRC__Alerter implements PluginAlerter {
138      private IRCBot _ircbot;
139      
140      /**
141 +     * Are we "active"
142 +     */
143 +    private boolean _active = false;
144 +    
145 +    /**
146 +     * The last alert that was sent
147 +     */
148 +    private String _lastAlert = "no alerts have been sent";
149 +    
150 +    /**
151 +     * The time of the last alert
152 +     */
153 +    private long _lastAlertTime = -1;
154 +    
155 +    /**
156 +     * Number of alerts sent
157 +     */
158 +    private int _alertCount = 0;
159 +    
160 +    /**
161 +     * Number of alerts ignored when in "stopped" mode
162 +     */
163 +    private int _ignoredCount = 0;
164 +    
165 +    /**
166 +     * Time of IRCBot startup
167 +     */
168 +    private long _startTime;
169 +    
170 +    /**
171 +     * This holds a reference to the
172 +     * system logger that is being used.
173 +     */
174 +    protected Logger _logger = ReferenceManager.getInstance().getLogger();
175 +    
176 +    /**
177       * This is the friendly identifier of the
178       * component this class is running in.
179       * eg, a Filter may be called "filter1",
# Line 111 | Line 182 | public class IRC__Alerter implements PluginAlerter {
182       * can be placed here.  This name could also
183       * be changed to null for utility classes.
184       */
185 <    private String _name = ClientMain.NAME;
185 >    private String _name = "IRC";
186  
116    /**
117     * This holds a reference to the
118     * system logger that is being used.
119     */
120    private Logger _logger = ReferenceManager.getInstance().getLogger();
121
187   //---STATIC ATTRIBUTES---
188  
189   //---INNER CLASSES---
190  
191 <    /**
127 <     * This class provides some basic IRCBot functionality. It connects
128 <     * to a specified server, and will remain there until told to
129 <     * leave. Whilst connected it can send a message or a notice to
130 <     * the server.
131 <     */
132 <    class IRCBot extends Thread {
191 >    class IRCBot extends PircBot implements Runnable {
192          
193          /**
194 <         * Main thread loop, this part of the class listens for
136 <         * messages from the server, and acts accordingly. At the
137 <         * present moment it only responds to pings.
194 >         * The default reconnect delay in seconds
195           */
196 +        public final int DEFAULT_RECONNECT_DELAY = 30;
197 +        
198 +        /**
199 +         * Attempt to kick-start the IRCBot into action. Will
200 +         * keep calling init() until it doesn't throw an
201 +         * an IOException (which will only be thrown if there
202 +         * is an error initialising). After a successful call
203 +         * to init() the method finishes.
204 +         */
205          public void run() {
206 <            // flag so we can stop the loop
141 <            boolean run = true;
142 <            while(run) {
206 >            while(true) {
207                  try {
208 <                    // read a command
209 <                    String cmd = _socketIn.readLine();
146 <                    // if it's a PING...
147 <                    if(cmd.startsWith("PING")) {
148 <                        // ...send a PONG
149 <                        _socketOut.println("PONG" + cmd.substring(4));
150 <                    }
151 <                } catch (IOException e) {
152 <                    // comms failure, lets stop
153 <                    _logger.write(this.toString(), Logger.ERROR, "Comms error: "+e);
154 <                    //run = false;
208 >                    init();
209 >                    break;
210                  }
211 +                catch (IOException e) {
212 +                    _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e);
213 +                    // wait for a while, defined in the config
214 +                    reconnectSleep();
215 +                }
216              }
217          }
218          
219          /**
220 <         * Sends a message to the channel.
220 >         * Connects the bot to an irc server and joins a channel,
221 >         * using details supplied from the i-scream configuration
222 >         * system. If this method completes without an exception
223 >         * one can presume the bot is ready for use.
224           *
225 <         * @param msg The message to send
225 >         * @throws IOException if there is any problem initialising
226           */
227 <        public void sendMsg(String msg) {
228 <            _socketOut.println("PRIVMSG "+_channel+" :"+msg);
227 >        private void init() throws IOException {
228 >            _logger.write(this.toString(), Logger.DEBUG, "Initialising IRCBot...");
229 >            
230 >            // get a hook on the configuration system
231 >            ConfigurationProxy cp = ConfigurationProxy.getInstance();
232 >            
233 >            // get hold of the server details
234 >            String server;
235 >            int port;
236 >            try {
237 >                server = cp.getProperty(_name, "Alerter.IRC.IRCServer");
238 >                port = Integer.parseInt(cp.getProperty(_name, "Alerter.IRC.IRCPort"));
239 >            } catch (PropertyNotFoundException e) {
240 >                _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
241 >                throw new IOException("Can't get irc server details due to configuration error");
242 >            } catch (NumberFormatException e) {
243 >                _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
244 >                throw new IOException("Can't get irc server details due to malformed configuration");
245 >            }
246 >            
247 >            // get hold of the user details and nickname list
248 >            String user, nickList;
249 >            try {
250 >                user = cp.getProperty(_name, "Alerter.IRC.user");
251 >                nickList = cp.getProperty(_name, "Alerter.IRC.nickList");
252 >            } catch (PropertyNotFoundException e) {
253 >                _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
254 >                throw new IOException("Can't get user/nickname details due to configuration error");
255 >            }
256 >            
257 >            // get hold of comment and finger information
258 >            //  -- we're not too fussed if these aren't set
259 >            String comment = "Alerter.IRC.comment is undefined";
260 >            String finger = "Alerter.IRC.finger is undefined";
261 >            try {
262 >                comment = cp.getProperty(_name, "Alerter.IRC.comment");
263 >                finger = cp.getProperty(_name, "Alerter.IRC.finger");
264 >            } catch (PropertyNotFoundException e) {
265 >                _logger.write(this.toString(), Logger.WARNING, "Configuration warning, using default: "+e);
266 >            }
267 >            
268 >            // put these details into the bot
269 >            this.setLogin(user);
270 >            this.setVersion("[" + getRevision() + "] " + comment);
271 >            this.setFinger(finger);
272 >            
273 >            // attempt to connect, trying each nickname
274 >            // in turn until sucessfully connected
275 >            StringTokenizer st = new StringTokenizer(nickList, ";");
276 >            boolean ok = false;
277 >            while(!ok && st.hasMoreTokens()) {
278 >                String nick = st.nextToken();
279 >                try {
280 >                    // try to connect with a nickname
281 >                    _logger.write(this.toString(), Logger.DEBUG, "Trying nick: "+nick);
282 >                    this.setName(nick);
283 >                    this.connect(server, port);
284 >                    // at this point we know the nickname was accepted
285 >                    _nickname = nick;
286 >                    ok = true;
287 >                }
288 >                catch(IOException e) {
289 >                    _logger.write(this.toString(), Logger.ERROR, "IO error when connecting to server: "+e);
290 >                    throw new IOException("IO error when connecting to server");
291 >                }
292 >                catch(IrcException e) {
293 >                    _logger.write(this.toString(), Logger.ERROR, "IRC error when connecting to server: "+e);
294 >                    throw new IOException("IRC error when connecting to server");
295 >                }
296 >                catch(NickAlreadyInUseException e) {
297 >                    _logger.write(this.toString(), Logger.ERROR, "Nickname "+nick+" is already in use: "+e);
298 >                    // don't do anything, instead just loop round
299 >                    // and try the next nickname in the list
300 >                }
301 >            }
302 >            if(!ok) {
303 >                // must have tried all the nicknames, best bail out
304 >                _logger.write(this.toString(), Logger.ERROR, "All nicknames already in use");
305 >                throw new IOException("All nicknames already in use");
306 >            }
307 >            
308 >            // get hold of the channel details, and attempt
309 >            // to join the specified channel
310 >            try {
311 >                _channel = cp.getProperty(_name, "Alerter.IRC.channel");
312 >                this.joinChannel(_channel);
313 >            } catch (PropertyNotFoundException e) {
314 >                _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
315 >                throw new IOException("Can't get channel name due to configuration error");
316 >            }
317 >            
318 >            // get hold of the startup notice, and send
319 >            // it if it's defined
320 >            String startupNotice;
321 >            try {
322 >                startupNotice = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice");
323 >                sendNotice(_channel, startupNotice);
324 >            } catch (PropertyNotFoundException e) {
325 >                _logger.write(this.toString(), Logger.DEBUG, "Startup notice not defined, so not sending: "+e);
326 >            }
327 >            
328 >            // at this point initialisation is complete, so
329 >            // we can return and set this flag to allow alerts
330 >            _active = true;
331          }
332          
333          /**
334 <         * Sends a notice to the channel.
334 >         * Send a message to the current channel.
335           *
336 <         * @param msg The notice to send
336 >         * @param message The message to send
337           */
338 <        public void sendNotice(String msg) {
339 <            _socketOut.println("NOTICE "+_channel+" :"+msg);
338 >        private void sendMessage(String message) {
339 >            sendMessage(_channel, message);
340          }
341          
342          /**
343 <         * Connect to the IRC server, log in, and join the channel.
343 >         * If we get disconnected this method will be
344 >         * called, so we must take action. We'll simply
345 >         * keep trying to reinitialise, and thus
346 >         * reconnect to the server.
347 >         */
348 >        public void onDisconnect() {
349 >            // stop alerts being sent for now
350 >            _active = false;
351 >            while(true) {
352 >                // wait for a while, defined in the config
353 >                reconnectSleep();
354 >                try {
355 >                    init();
356 >                    break;
357 >                }
358 >                catch (IOException e) {
359 >                    _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e);
360 >                }
361 >            }
362 >        }
363 >        
364 >        /**
365 >         * If we receive a message this method will
366 >         * be called. We'll check if the message is
367 >         * for us, and call handleInput if it is.
368           *
369 <         * @throws IOException if the connection fails
369 >         * @param channel The channel the message came from
370 >         * @param sender The sender of the message
371 >         * @param login The login of the sender
372 >         * @param hostname The hostname of the sender
373 >         * @param message The message sent
374           */
375 <        public void connect() throws IOException {
375 >        public void onMessage(String channel, String sender, String login, String hostname, String message) {
376 >            String trimmedMessage = isForMe(message);
377 >            // if trimmedMessage is null, it's not for us
378 >            if(trimmedMessage != null) {
379 >                handleInput(trimmedMessage, channel);
380 >            }
381 >        }
382 >        
383 >        /**
384 >         * If we receive a private message this method
385 >         * will be called. No need to check if it's for
386 >         * us -- it has to be if it's a private message.
387 >         *
388 >         * @param sender The sender of the message
389 >         * @param login The login of the sender
390 >         * @param hostname The hostname of the sender
391 >         * @param message The message sent
392 >         */
393 >        public void onPrivateMessage(String sender, String login, String hostname, String message) {
394 >            handleInput(message, sender);
395 >        }
396 >        
397 >        /**
398 >         * If we receive a nick change message, this
399 >         * method gets called. We don't care about
400 >         * other users changing their nick, so we only
401 >         * take notice if our nick changes. If it does
402 >         * we need to change our internal nickname
403 >         * state.
404 >         *
405 >         * @param oldNick the old nickname
406 >         * @param login the login of the nick changer
407 >         * @param hostname the hostname of the nick changer
408 >         * @param newNick the new nickname
409 >         */
410 >        public void onNickChange(String oldNick, String login, String hostname, String newNick) {
411 >            if(oldNick.equals(_nickname)) {
412 >                _nickname = newNick;
413 >                // tell the underlying pircbot that our nick has changed too :)
414 >                setName(newNick);
415 >            }
416 >        }
417 >        
418 >        /**
419 >         * If we receive a kick message this method
420 >         * gets called. We only take notice if it's
421 >         * us getting kicked, and then we'll rejoin
422 >         * the channel after a short wait.
423 >         *
424 >         * @param channel the channel the kick happened on
425 >         * @param kickerNick the person who performed the kick
426 >         * @param kickerLogin the login of the person who performed the kick
427 >         * @param kickerHostname the hostname of the person who performed the kick
428 >         * @param recipientNick the nickname of the person being kicked
429 >         * @param reason the reason for the kick
430 >         */
431 >        public void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason) {
432 >            if(recipientNick.equals(_nickname) && channel.equals(_channel)) {
433 >                // remind the person it's not very nice :)
434 >                sendMessage(kickerNick, "That wasn't a nice thing to do...");
435 >                // get hold of the channel details, in case they've changed
436 >                try {
437 >                    _channel = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.channel");
438 >                } catch(PropertyNotFoundException e) {
439 >                    _logger.write(this.toString(), Logger.ERROR, "Can't get channel name due to configuration error: "+e);
440 >                }
441 >                // wait for a while, defined in the config
442 >                reconnectSleep();
443 >                // we'll try and rejoin the channel regardless
444 >                // otherwise we might end up doing nothing!
445 >                joinChannel(_channel);
446 >            }
447 >        }
448 >        
449 >        /**
450 >         * This method handles input directed to us, and
451 >         * responds accordingly if required.
452 >         *
453 >         * @param message the message from someone
454 >         * @param source where the message came from, so we know where to send the response
455 >         */
456 >        private void handleInput(String message, String source) {
457 >            // get hold of the configuration system
458              ConfigurationProxy cp = ConfigurationProxy.getInstance();
459 <            // setup the socket, reader and writer
460 <            String server = cp.getProperty(_name, "Alerter.IRC.IRCServer");
461 <            int port = Integer.parseInt(cp.getProperty(_name, "Alerter.IRC.IRCPort"));
462 <            _socket = new Socket(server, port);
463 <            _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
464 <            _socketOut = new PrintWriter(_socket.getOutputStream(), true);
465 <            // send the various log in messages
466 <            _socketOut.println("PASS");
467 <            _socketOut.println("NICK "+cp.getProperty(_name, "Alerter.IRC.nick"));  
468 <            _socketOut.println("USER i-scream 8 * :i-scream alerter bot");
469 <            // join the channel
470 <            _channel = cp.getProperty(_name, "Alerter.IRC.channel");
471 <            _socketOut.println("JOIN "+_channel);
472 <            // start our listening thread
473 <            this.start();
459 >            // setup some String's
460 >            String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand;
461 >            String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand;
462 >            // get the command set from the configuration
463 >            try {
464 >                stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand");
465 >                startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand");
466 >                timeSinceLastAlertCommand = cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand");
467 >                lastAlertCommand = cp.getProperty(_name, "Alerter.IRC.lastAlertCommand");
468 >                joinCommand = cp.getProperty(_name, "Alerter.IRC.joinCommand");
469 >                nickChangeCommand = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand");
470 >                versionCommand = cp.getProperty(_name, "Alerter.IRC.versionCommand");
471 >                helpCommand = cp.getProperty(_name, "Alerter.IRC.helpCommand");
472 >                statCommand = cp.getProperty(_name, "Alerter.IRC.statCommand");
473 >                uptimeCommand = cp.getProperty(_name, "Alerter.IRC.uptimeCommand");
474 >            } catch (PropertyNotFoundException e) {
475 >                _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
476 >                // lets bail from handling this line...
477 >                // ...it's gonna be hard without a command set!
478 >                return;
479 >            }
480 >            
481 >            if(message.equalsIgnoreCase(stopCommand)) {
482 >                _active = false;
483 >                sendMessage(source, "alerts have been stopped");
484 >            }
485 >            else if(message.equalsIgnoreCase(startCommand)) {
486 >                _active = true;
487 >                sendMessage(source, "alerts have been activated");
488 >            }
489 >            // this needs to go before lastAlertCommand if it contains
490 >            // the same words as the lastAlertCommand.
491 >            else if(message.equalsIgnoreCase(timeSinceLastAlertCommand)) {
492 >                if(_lastAlertTime != -1) {
493 >                    long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
494 >                    String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
495 >                    sendMessage(source, "I last sent an alert "+uptimeText+ " ago");
496 >                }
497 >                else {
498 >                    sendMessage(source, "I've never sent an alert!");
499 >                }
500 >            }
501 >            else if(message.equalsIgnoreCase(lastAlertCommand)) {
502 >                if(_lastAlertTime != -1) {
503 >                    String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
504 >                    sendMessage(source, "last alert was at "+date+"; "+_lastAlert);
505 >                }
506 >                else {
507 >                    sendMessage(source, "I've never sent an alert!");
508 >                }
509 >                
510 >            }
511 >            else if(message.toLowerCase().startsWith(joinCommand.toLowerCase())) {
512 >                String joinCmd = joinCommand;
513 >                String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
514 >                int endOfChan = newChan.indexOf(" ");
515 >                if(endOfChan == -1) {
516 >                    endOfChan = newChan.length();
517 >                }
518 >                newChan = newChan.substring(0, endOfChan);
519 >                if(newChan.equals(_channel)) {
520 >                    sendMessage(source, "I'm already on "+newChan+"!");
521 >                } else {
522 >                    partChannel(_channel);
523 >                    joinChannel(newChan);
524 >                    _channel = newChan;
525 >                }
526 >            }
527 >            else if(message.toLowerCase().startsWith(nickChangeCommand.toLowerCase())) {
528 >                String nickChangeCmd = nickChangeCommand;
529 >                String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
530 >                int endOfNick = newNick.indexOf(" ");
531 >                if(endOfNick == -1) {
532 >                    endOfNick = newNick.length();
533 >                }
534 >                newNick = newNick.substring(0, endOfNick);
535 >                changeNick(newNick);
536 >            }
537 >            else if(message.equalsIgnoreCase(versionCommand)) {
538 >                sendMessage(source, "I am version " + getRevision() + " of the i-scream alerting bot");
539 >            }
540 >            else if(message.equalsIgnoreCase(helpCommand)) {
541 >                sendMessage(source, "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
542 >                sendMessage(source, "I understand the following commands;");
543 >                sendMessage(source, stopCommand);
544 >                sendMessage(source, startCommand);
545 >                sendMessage(source, lastAlertCommand);
546 >                sendMessage(source, joinCommand);
547 >                sendMessage(source, nickChangeCommand);
548 >                sendMessage(source, statCommand);
549 >                sendMessage(source, uptimeCommand);
550 >                sendMessage(source, timeSinceLastAlertCommand);
551 >                sendMessage(source, helpCommand);
552 >            }
553 >            else if(message.equalsIgnoreCase(statCommand)) {
554 >                sendMessage(source, "I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!");
555 >            }
556 >            else if(message.equalsIgnoreCase(uptimeCommand)) {
557 >                long uptime = (System.currentTimeMillis() - _startTime) / 1000;
558 >                String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
559 >                sendMessage(source, "I have been running for "+uptimeText);
560 >            }
561 >            else if(message.equalsIgnoreCase("ping")) {
562 >                sendMessage(source, "pong");
563 >            }
564 >            else if(message.equalsIgnoreCase("do a jibble dance")) {
565 >                // little joke :)
566 >                sendAction(source, "jives to the funky beat shouting \"ii--screeeaaammm\"");
567 >            }
568 >            else {
569 >                String rejectMessage = NOT_CONFIGURED;
570 >                try {
571 >                    rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage");
572 >                } catch(PropertyNotFoundException e) {
573 >                    _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
574 >                }
575 >                sendMessage(source, rejectMessage);
576 >            }
577          }
578          
579          /**
580 <         * Disconnect "nicely" from the IRC server.
580 >         * Quick method to check if the message appears
581 >         * to be directed at us. We simply check to see
582 >         * if it starts with our nick in some fashion.
583 >         * This will return null if the message is not
584 >         * for us - thus allowing this method to perform
585 >         * two tasks at once.
586           *
587 <         * @throws IOException if the disconnection fails
587 >         * @param message the message to check
588 >         * @return the message (with our nick removed), or null if it's not for us.
589           */
590 <        public void disconnect() throws IOException {
591 <            // send proper QUIT
592 <            _socketOut.println("QUIT : iscreamBot component shutting down...");
593 <            // close the socket
594 <            _socketOut.close();
595 <            _socketIn.close();
596 <            _socket.close();
590 >        private String isForMe(String message) {
591 >            // change to lower case to remove
592 >            // case ambiguities
593 >            String nick = _nickname.toLowerCase();
594 >            String msg = message.toLowerCase();
595 >            if(msg.startsWith(nick + ", ") ||
596 >               msg.startsWith(nick + ": ") ||
597 >               msg.startsWith(nick + " ")) {
598 >                // NOTE that the 1 here is hardcoded to be the length
599 >                // of the above 'extra' character (eg , or :)
600 >                return message.substring(nick.length()+1).trim();
601 >            }
602 >            else {
603 >                return null;
604 >            }
605          }
606          
607          /**
608 <         * The socket connected to the server
608 >         * Sleep for a configurable amount of time and
609 >         * then return. This is used when reconnecting
610 >         * to the server or a channel.
611           */
612 <        private Socket _socket;
612 >        private void reconnectSleep() {
613 >            int delayTime = 0;
614 >            try {
615 >                delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay"));
616 >            } catch (NumberFormatException e) {
617 >                delayTime = DEFAULT_RECONNECT_DELAY;
618 >                _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds");
619 >            } catch (PropertyNotFoundException e) {
620 >                delayTime = DEFAULT_RECONNECT_DELAY;
621 >                _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds");
622 >            }
623 >            _logger.write(this.toString(), Logger.ERROR, "Waiting "+delayTime+" seconds for reconnect...");
624 >            try {
625 >                Thread.sleep(delayTime * 1000);
626 >            } catch (InterruptedException e) {}
627 >        }
628          
629          /**
630 <         * The writer
630 >         * Returns the revision of the bot.
631 >         *
632 >         * @return the revision of this bot
633           */
634 <        private PrintWriter _socketOut;
634 >        private String getRevision() {
635 >            return REVISION.substring(11, REVISION.length()-2);
636 >        }
637          
638          /**
639 <         * The reader
639 >         * Overrides the {@link java.lang.Object#toString() Object.toString()}
640 >         * method to provide clean logging (every class should have this).
641 >         *
642 >         * This uses the uk.org.iscream.cms.server.util.NameFormat class
643 >         * to format the toString()
644 >         *
645 >         * @return the name of this class and its CVS revision
646           */
647 <        private BufferedReader _socketIn;
647 >        public String toString() {
648 >            return FormatName.getName(
649 >                _name,
650 >                getClass().getName(),
651 >                REVISION);
652 >        }
653          
654          /**
655           * Just a reminder to what channel we're on...
656           * this can't be dynamic :)
657           */
658          private String _channel;
659 +        
660 +        /**
661 +         * A reminder of our current nickname...
662 +         */
663 +        private String _nickname;
664 +        
665      }
666  
667   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines