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.27 by tdb, Sat Mar 24 18:43:38 2001 UTC vs.
Revision 1.32 by tdb, Sat May 18 18:16:00 2002 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines