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.25 by ajm, Fri Mar 23 01:09:51 2001 UTC vs.
Revision 1.30.2.5 by tdb, Tue Feb 5 22:39:29 2002 UTC

# Line 1 | Line 1
1   //---PACKAGE DECLARATION---
2 < package uk.org.iscream.client.alerters;
2 > package uk.org.iscream.cms.server.client.alerters;
3  
4   //---IMPORTS---
5 < import uk.org.iscream.client.*;
6 < import uk.org.iscream.core.*;
7 < import uk.org.iscream.util.*;
8 < import uk.org.iscream.componentmanager.*;
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.
# Line 34 | Line 35 | public class IRC__Alerter extends AlerterSkeleton {
35       */
36      public final String DESC = "Sends alerts on an IRC channel";
37      
37    /**
38     * The default reconnect delay in seconds
39     */
40    public final int DEFAULT_RECONNECT_DELAY = 30;
41    
38   //---STATIC METHODS---
39  
40   //---CONSTRUCTORS---
41 <
41 >    
42      public IRC__Alerter() {
43          super();
44 <        // connect to the IRC server
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 <        _ircbot.setName("client.IRC__Alerter$IRCBot");
50 <        _ircbot.start();
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      }
# Line 64 | Line 63 | public class IRC__Alerter extends AlerterSkeleton {
63       * @param alert the alert to send
64       */
65      public void sendAlert(Alert alert) {
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) {
69 <            // sort out the message      
70 <            String alertType = Alert.alertLevels[alert.getLevel()];        
71 <            String message;
72 <            try {
73 <                message = _cp.getProperty(_name, "Alerter.IRC.message");
74 <            } catch (PropertyNotFoundException e) {
75 <                message = NOT_CONFIGURED;
76 <                _logger.write(toString(), Logger.WARNING, "Alerter.IRC.message value unavailable using default of " + message);
77 <            }
78 <            message = processAlertMessage(message, alert);                
79 <            
77 >        if(_active) {          
78              // send the message
79              _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level");
80 <            _ircbot.sendMsg(message);
81 <            _lastAlert = message;
82 <            _lastAlertTime = System.currentTimeMillis();
85 <            _alertCount ++;
80 >            _ircbot.sendMessage(message);
81 >            // count sent alerts
82 >            _alertCount++;
83          } else {
84 <            _ignoredCount ++;
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.org.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 117 | Line 119 | public class IRC__Alerter extends AlerterSkeleton {
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 155 | Line 168 | public class IRC__Alerter extends AlerterSkeleton {
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 169 | Line 188 | public class IRC__Alerter extends AlerterSkeleton {
188  
189   //---INNER CLASSES---
190  
191 <    /**
173 <     * This class provides some basic IRCBot functionality. It connects
174 <     * to a specified server, and will remain there until told to
175 <     * leave. Whilst connected it can send a message or a notice to
176 <     * the server.
177 <     */
178 <    class IRCBot extends Thread {
191 >    class IRCBot extends PircBot implements Runnable {
192          
193 <        public static final String DEFAULT_STARTUP_NOTICE = "i-scream ircbot starting...";
193 >        /**
194 >         * The default reconnect delay in seconds
195 >         */
196 >        public final int DEFAULT_RECONNECT_DELAY = 30;
197          
198          /**
199 <         * Main thread loop, this part of the class listens for
200 <         * messages from the server, and acts accordingly. At the
201 <         * present moment it only responds to pings.
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 <            // so we can stop if required
189 <            boolean run = true;
190 <            while(run) {
191 <                // flag so we can stop the loop
192 <                boolean doRead = true;
193 <                // get the startup notice
194 <                String startupNotice;
206 >            while(true) {
207                  try {
208 <                    startupNotice = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice");
209 <                } catch (PropertyNotFoundException e) {
198 <                    startupNotice = DEFAULT_STARTUP_NOTICE;
199 <                    _logger.write(this.toString(), Logger.WARNING, "Configuration error: "+e);
208 >                    init();
209 >                    break;
210                  }
211 <                // connect to the IRC server
212 <                try {
213 <                    connect();
214 <                    sendNotice(startupNotice);
205 <                } catch(IOException e) {
206 <                    doRead=false;
207 <                    _logger.write(this.toString(), Logger.ERROR, "Error connecting: "+e);
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                  }
209                while(doRead) {
210                    try {
211                        // read a command
212                        String cmd = _socketIn.readLine();
213                        // if we have a null, we've lost contact
214                        if(cmd == null) {
215                            throw new IOException("End of stream reached");
216                        }
217                        // let another method deal with the input
218                        handleInput(cmd);
219                    } catch (IOException e) {
220                        // comms failure, maybe our link is dead.
221                        _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e);
222                        // stop, and loop round for a reconnect.
223                        doRead = false;
224                    }
225                }
226                // make sure we're disconnected
227                try {
228                    disconnect();
229                } catch (IOException e) {
230                    _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e);
231                }
232                
233                // comms have failed, so wait a while and reconnect
234                int delayTime = 0;
235                try {
236                    delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay"));
237                } catch (NumberFormatException e) {
238                    delayTime = DEFAULT_RECONNECT_DELAY;
239                    _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds");
240                } catch (PropertyNotFoundException e) {
241                    delayTime = DEFAULT_RECONNECT_DELAY;
242                    _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds");
243                }
244                try {
245                    Thread.sleep(delayTime * 1000);
246                } catch (InterruptedException e) {}
216              }
248            // maybe disconnect here ? - shutdown method not implemented yet
249            //disconnect();
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);
229 <            // wait a second before returning...
230 <            // this ensures messages can't be sent too fast
261 <            try {Thread.sleep(1000);} catch (InterruptedException e) {}
262 <        }
263 <        
264 <        /**
265 <         * Sends a message to the channel.
266 <         *
267 <         * @param user The user to send to
268 <         * @param msg The message to send
269 <         */
270 <        public void sendPrivMsg(String user, String msg) {
271 <            _socketOut.println("PRIVMSG "+user+" :"+msg);
272 <            // wait a second before returning...
273 <            // this ensures messages can't be sent too fast
274 <            try {Thread.sleep(1000);} catch (InterruptedException e) {}
275 <        }
276 <        
277 <        /**
278 <         * Sends an action to the channel.
279 <         *
280 <         * @param msg the action message
281 <         */
282 <        public void sendAction(String msg) {
283 <            char esc = 001;
284 <            sendMsg(esc+"ACTION "+msg+esc);
285 <            // wait a second before returning...
286 <            // this ensures messages can't be sent too fast
287 <            try {Thread.sleep(1000);} catch (InterruptedException e) {}
288 <        }
289 <        
290 <        /**
291 <         * Sends a notice to the channel.
292 <         *
293 <         * @param msg The notice to send
294 <         */
295 <        public void sendNotice(String msg) {
296 <            _socketOut.println("NOTICE "+_channel+" :"+msg);
297 <            // wait a second before returning...
298 <            // this ensures messages can't be sent too fast
299 <            try {Thread.sleep(1000);} catch (InterruptedException e) {}
300 <        }
301 <        
302 <        /**
303 <         * Connect to the IRC server, log in, and join the channel.
304 <         *
305 <         * @throws IOException if the connection fails
306 <         */
307 <        public void connect() throws IOException {
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 <            // setup the socket, reader and writer
232 >            
233 >            // get hold of the server details
234              String server;
235              int port;
236              try {
# Line 319 | Line 243 | public class IRC__Alerter extends AlerterSkeleton {
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 <            _socket = new Socket(server, port);
247 <            _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
248 <            _socketOut = new PrintWriter(_socket.getOutputStream(), true);
325 <            //_socketOut.println("PASS");
326 <            // send USER details
327 <            String user, comment;
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 <                comment = cp.getProperty(_name, "Alerter.IRC.comment");
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 details due to configuration error");
254 >                throw new IOException("Can't get user/nickname details due to configuration error");
255              }
256 <            _socketOut.println("USER "+user+" 8 * :"+comment);
257 <            // attempt to get a nick
258 <            String nickList;
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 <                nickList = cp.getProperty(_name, "Alerter.IRC.nickList");
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.ERROR, "Configuration error: "+e);
342 <                throw new IOException("Can't get nickname due to configuration error");
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;
346            // try until we exhaust our list
277              while(!ok && st.hasMoreTokens()) {
278                  String nick = st.nextToken();
279 <                _socketOut.println("NICK "+nick);
280 <                // get a "yes" or "no" response back
281 <                String response = "";
282 <                do {
283 <                    response = _socketIn.readLine();
284 <                    if(response==null) {
355 <                        throw new IOException("Communication error whilst logging in");
356 <                    }
357 <                } while(response.indexOf("001")==-1 && response.indexOf("433")==-1);
358 <                // see if it was a yes
359 <                if(response.indexOf("001")!=-1) {
360 <                    // great, we're logged in !
361 <                    ok = true;
362 <                    // store the name we're using
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 <                else {
289 <                    // log we couldn't get the name
290 <                    _logger.write(this.toString(), Logger.WARNING, "Nickname in use: "+nick);
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 <                // oh dear, we couldn't get on.
304 <                throw new IOException("All nicknames in use");
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 <            // join the channel
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 <            _socketOut.println("JOIN "+_channel);
318 <            // allow alerts
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 <         * Disconnect "nicely" from the IRC server.
334 >         * Send a message to the current channel.
335           *
336 <         * @throws IOException if the disconnection fails
336 >         * @param message The message to send
337           */
338 <        public void disconnect() throws IOException {
339 <            // stop alerts
338 >        private void sendMessage(String message) {
339 >            sendMessage(_channel, message);
340 >        }
341 >        
342 >        /**
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 <            // send proper QUIT
352 <            _socketOut.println("QUIT : iscreamBot component shutting down...");
353 <            // close the socket
354 <            _socketOut.close();
355 <            _socketIn.close();
356 <            _socket.close();
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 <         * Overrides the {@link java.lang.Object#toString() Object.toString()}
366 <         * method to provide clean logging (every class should have this).
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 <         * This uses the uk.org.iscream.util.NameFormat class
370 <         * to format the toString()
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 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 <         * @return the name of this class and its CVS revision
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 String toString() {
394 <            return FormatName.getName(
413 <                _name,
414 <                getClass().getName(),
415 <                REVISION);
393 >        public void onPrivateMessage(String sender, String login, String hostname, String message) {
394 >            handleInput(message, sender);
395          }
396          
397          /**
398 <         * Deals with incoming lines from the server.
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 line the line to deal with
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 <        private void handleInput(String line) {
411 <            ConfigurationProxy cp = ConfigurationProxy.getInstance();
412 <            // if it's a PING...
426 <            if(line.startsWith("PING")) {
427 <                // ...send a PONG
428 <                _socketOut.println("PONG" + line.substring(4));
410 >        public void onNickChange(String oldNick, String login, String hostname, String newNick) {
411 >            if(oldNick.equals(_nickname)) {
412 >                _nickname = newNick;
413              }
414 <            // see if it's for us
415 <            else if(getMsg(line).startsWith(_nickname+",") || getMsg(line).startsWith(_nickname+":") || getMsg(line).startsWith(_nickname+" ")) {
416 <                // setup some String's
417 <                String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand;
418 <                String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand;
419 <                // get the command set
414 >        }
415 >        
416 >        /**
417 >         * If we receive a kick message this method
418 >         * gets called. We only take notice if it's
419 >         * us getting kicked, and then we'll rejoin
420 >         * the channel after a short wait.
421 >         *
422 >         * @param channel the channel the kick happened on
423 >         * @param kickerNick the person who performed the kick
424 >         * @param kickerLogin the login of the person who performed the kick
425 >         * @param kickerHostname the hostname of the person who performed the kick
426 >         * @param recipientNick the nickname of the person being kicked
427 >         * @param reason the reason for the kick
428 >         */
429 >        public void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason) {
430 >            if(recipientNick.equals(_nickname) && channel.equals(_channel)) {
431 >                // remind the person it's not very nice :)
432 >                sendMessage(kickerNick, "That wasn't a nice thing to do...");
433 >                // get hold of the channel details, in case they've changed
434                  try {
435 <                    stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand");
436 <                    startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand");
437 <                    timeSinceLastAlertCommand = cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand");
440 <                    lastAlertCommand = cp.getProperty(_name, "Alerter.IRC.lastAlertCommand");
441 <                    joinCommand = cp.getProperty(_name, "Alerter.IRC.joinCommand");
442 <                    nickChangeCommand = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand");
443 <                    versionCommand = cp.getProperty(_name, "Alerter.IRC.versionCommand");
444 <                    helpCommand = cp.getProperty(_name, "Alerter.IRC.helpCommand");
445 <                    statCommand = cp.getProperty(_name, "Alerter.IRC.statCommand");
446 <                    uptimeCommand = cp.getProperty(_name, "Alerter.IRC.uptimeCommand");
447 <                } catch (PropertyNotFoundException e) {
448 <                    _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
449 <                    // lets bail from handling this line...
450 <                    // ...it's gonna be hard without a command set!
451 <                    return;
435 >                    _channel = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.channel");
436 >                } catch(PropertyNotFoundException e) {
437 >                    _logger.write(this.toString(), Logger.ERROR, "Can't get channel name due to configuration error: "+e);
438                  }
439 <                
440 <                // we have a message for us
441 <                String message = getMsg(line).substring(_nickname.length());
442 <                if(message.indexOf(stopCommand)!=-1) {
443 <                    _active = false;
444 <                    sendMsg(getMsgSender(line)+", alerts have been stopped");
439 >                // wait for a while, defined in the config
440 >                reconnectSleep();
441 >                // we'll try and rejoin the channel regardless
442 >                // otherwise we might end up doing nothing!
443 >                joinChannel(_channel);
444 >            }
445 >        }
446 >        
447 >        /**
448 >         * This method handles input directed to us, and
449 >         * responds accordingly if required.
450 >         *
451 >         * nb. matching user input is quite bad right now,
452 >         *     and should be improved one day.
453 >         *
454 >         * @param message the message from someone
455 >         * @param source where the message came from, so we know where to send the response
456 >         */
457 >        private void handleInput(String message, String source) {
458 >            // get hold of the configuration system
459 >            ConfigurationProxy cp = ConfigurationProxy.getInstance();
460 >            // setup some String's
461 >            String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand;
462 >            String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand;
463 >            // get the command set from the configuration
464 >            try {
465 >                stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand");
466 >                startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand");
467 >                timeSinceLastAlertCommand = cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand");
468 >                lastAlertCommand = cp.getProperty(_name, "Alerter.IRC.lastAlertCommand");
469 >                joinCommand = cp.getProperty(_name, "Alerter.IRC.joinCommand");
470 >                nickChangeCommand = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand");
471 >                versionCommand = cp.getProperty(_name, "Alerter.IRC.versionCommand");
472 >                helpCommand = cp.getProperty(_name, "Alerter.IRC.helpCommand");
473 >                statCommand = cp.getProperty(_name, "Alerter.IRC.statCommand");
474 >                uptimeCommand = cp.getProperty(_name, "Alerter.IRC.uptimeCommand");
475 >            } catch (PropertyNotFoundException e) {
476 >                _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
477 >                // lets bail from handling this line...
478 >                // ...it's gonna be hard without a command set!
479 >                return;
480 >            }
481 >            
482 >            // see if the message matches (loosely!) any
483 >            // of our known commands
484 >            if(message.equalsIgnoreCase(stopCommand)) {
485 >                _active = false;
486 >                sendMessage(source, "alerts have been stopped");
487 >            }
488 >            else if(message.equalsIgnoreCase(startCommand)) {
489 >                _active = true;
490 >                sendMessage(source, "alerts have been activated");
491 >            }
492 >            // this needs to go before lastAlertCommand if it contains
493 >            // the same words as the lastAlertCommand.
494 >            else if(message.equalsIgnoreCase(timeSinceLastAlertCommand)) {
495 >                if(_lastAlertTime != -1) {
496 >                    long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
497 >                    String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
498 >                    sendMessage(source, "I last sent an alert "+uptimeText+ " ago");
499                  }
500 <                else if(message.indexOf(startCommand)!=-1) {
501 <                    _active = true;
462 <                    sendMsg(getMsgSender(line)+", alerts have been activated");
500 >                else {
501 >                    sendMessage(source, "I've never sent an alert!");
502                  }
503 <                // this needs to go here if it contains the same words as the lastAlertCommand
504 <                else if(message.indexOf(timeSinceLastAlertCommand)!=-1) {
505 <                    if(_lastAlertTime != -1) {
506 <                        long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
507 <                        String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
469 <                        sendMsg(getMsgSender(line)+", I last sent an alert "+uptimeText+ " ago");
470 <                    }
471 <                    else {
472 <                        sendMsg(getMsgSender(line)+", I've never sent an alert!");
473 <                    }
503 >            }
504 >            else if(message.equalsIgnoreCase(lastAlertCommand)) {
505 >                if(_lastAlertTime != -1) {
506 >                    String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
507 >                    sendMessage(source, "last alert was at "+date+"; "+_lastAlert);
508                  }
509 <                else if(message.indexOf(lastAlertCommand)!=-1) {
510 <                    if(_lastAlertTime != -1) {
477 <                        String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
478 <                        sendMsg(getMsgSender(line)+", last alert was at "+date+"; "+_lastAlert);
479 <                    }
480 <                    else {
481 <                        sendMsg(getMsgSender(line)+", I've never sent an alert!");
482 <                    }
483 <                    
509 >                else {
510 >                    sendMessage(source, "I've never sent an alert!");
511                  }
512 <                else if(message.indexOf(joinCommand)!=-1) {
513 <                    String joinCmd = joinCommand;
514 <                    String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
515 <                    int endOfChan = newChan.indexOf(" ");
516 <                    if(endOfChan == -1) {
517 <                        endOfChan = newChan.length();
518 <                    }
519 <                    newChan = newChan.substring(0, endOfChan);
493 <                    if(newChan.equals(_channel)) {
494 <                        sendMsg(getMsgSender(line)+", I'm already on "+newChan+"!");
495 <                    } else {
496 <                        sendMsg(getMsgSender(line)+", okay, I'm off to "+newChan);
497 <                        _socketOut.println("PART "+_channel);
498 <                        _socketOut.println("JOIN "+newChan);
499 <                        _channel = newChan;
500 <                    }
512 >                
513 >            }
514 >            else if(message.equalsIgnoreCase(joinCommand)) {
515 >                String joinCmd = joinCommand;
516 >                String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
517 >                int endOfChan = newChan.indexOf(" ");
518 >                if(endOfChan == -1) {
519 >                    endOfChan = newChan.length();
520                  }
521 <                else if(message.indexOf(nickChangeCommand)!=-1) {
522 <                    String nickChangeCmd = nickChangeCommand;
523 <                    String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
524 <                    int endOfNick = newNick.indexOf(" ");
525 <                    if(endOfNick == -1) {
526 <                        endOfNick = newNick.length();
527 <                    }
509 <                    newNick = newNick.substring(0, endOfNick);
510 <                    sendMsg(getMsgSender(line)+", okay, changing my nickname to "+newNick);
511 <                    _socketOut.println("NICK "+newNick);
512 <                    _nickname = newNick;
521 >                newChan = newChan.substring(0, endOfChan);
522 >                if(newChan.equals(_channel)) {
523 >                    sendMessage(source, "I'm already on "+newChan+"!");
524 >                } else {
525 >                    partChannel(_channel);
526 >                    joinChannel(newChan);
527 >                    _channel = newChan;
528                  }
529 <                else if(message.indexOf(versionCommand)!=-1) {
530 <                    sendMsg(getMsgSender(line)+", I am version "+REVISION.substring(11, REVISION.length() -2)+" of the i-scream alerting bot");
529 >            }
530 >            else if(message.equalsIgnoreCase(nickChangeCommand)) {
531 >                String nickChangeCmd = nickChangeCommand;
532 >                String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
533 >                int endOfNick = newNick.indexOf(" ");
534 >                if(endOfNick == -1) {
535 >                    endOfNick = newNick.length();
536                  }
537 <                else if(message.indexOf(helpCommand)!=-1) {
538 <                    sendPrivMsg(getMsgSender(line), "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
519 <                    sendPrivMsg(getMsgSender(line), "I understand the following commands;");
520 <                    sendPrivMsg(getMsgSender(line), stopCommand);
521 <                    sendPrivMsg(getMsgSender(line), startCommand);
522 <                    sendPrivMsg(getMsgSender(line), lastAlertCommand);
523 <                    sendPrivMsg(getMsgSender(line), joinCommand);
524 <                    sendPrivMsg(getMsgSender(line), nickChangeCommand);
525 <                    sendPrivMsg(getMsgSender(line), statCommand);
526 <                    sendPrivMsg(getMsgSender(line), uptimeCommand);
527 <                    sendPrivMsg(getMsgSender(line), timeSinceLastAlertCommand);
528 <                    sendPrivMsg(getMsgSender(line), helpCommand);
529 <                }
530 <                else if(message.indexOf(statCommand)!=-1) {
531 <                    sendMsg(getMsgSender(line)+", I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!");
532 <                }
533 <                else if(message.indexOf(uptimeCommand)!=-1) {
534 <                    long uptime = (System.currentTimeMillis() - _startTime) / 1000;
535 <                    String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
536 <                    sendMsg(getMsgSender(line)+", I have been running for "+uptimeText);
537 <                }
538 <                else if(message.indexOf("ping")!=-1) {
539 <                    sendMsg("pong");
540 <                }
541 <                else if(message.indexOf("do a jibble dance")!=-1) {
542 <                    // little joke :)
543 <                    sendAction("jives to the funky beat shouting \"ii--screeeaaammm\"");
544 <                }
545 <                else {
546 <                    String rejectMessage = NOT_CONFIGURED;
547 <                    try {
548 <                        rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage");
549 <                    } catch(PropertyNotFoundException e) {
550 <                        _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
551 <                    }
552 <                    sendMsg(getMsgSender(line)+", "+rejectMessage);
553 <                }
537 >                newNick = newNick.substring(0, endOfNick);
538 >                changeNick(newNick);
539              }
540 <            else if(line.indexOf(_nickname)!=-1 && line.indexOf(_channel)!=-1 && line.indexOf("KICK")!=-1) {
541 <                sendPrivMsg(getMsgSender(line), "That wasn't a nice thing to do...");
540 >            else if(message.equalsIgnoreCase(versionCommand)) {
541 >                sendMessage(source, "I am version " + getRevision() + " of the i-scream alerting bot");
542 >            }
543 >            else if(message.equalsIgnoreCase(helpCommand)) {
544 >                sendMessage(source, "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
545 >                sendMessage(source, "I understand the following commands;");
546 >                sendMessage(source, stopCommand);
547 >                sendMessage(source, startCommand);
548 >                sendMessage(source, lastAlertCommand);
549 >                sendMessage(source, joinCommand);
550 >                sendMessage(source, nickChangeCommand);
551 >                sendMessage(source, statCommand);
552 >                sendMessage(source, uptimeCommand);
553 >                sendMessage(source, timeSinceLastAlertCommand);
554 >                sendMessage(source, helpCommand);
555 >            }
556 >            else if(message.equalsIgnoreCase(statCommand)) {
557 >                sendMessage(source, "I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!");
558 >            }
559 >            else if(message.equalsIgnoreCase(uptimeCommand)) {
560 >                long uptime = (System.currentTimeMillis() - _startTime) / 1000;
561 >                String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
562 >                sendMessage(source, "I have been running for "+uptimeText);
563 >            }
564 >            else if(message.equalsIgnoreCase("ping")) {
565 >                sendMessage(source, "pong");
566 >            }
567 >            else if(message.equalsIgnoreCase("do a jibble dance")) {
568 >                // little joke :)
569 >                sendAction(source, "jives to the funky beat shouting \"ii--screeeaaammm\"");
570 >            }
571 >            else {
572 >                String rejectMessage = NOT_CONFIGURED;
573                  try {
574 <                    _channel = cp.getProperty(_name, "Alerter.IRC.channel");
574 >                    rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage");
575                  } catch(PropertyNotFoundException e) {
576                      _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
577                  }
578 <                _socketOut.println("JOIN "+_channel);
578 >                sendMessage(source, rejectMessage);
579              }
580          }
581          
582          /**
583 <         * Strips the header from a message line
583 >         * Quick method to check if the message appears
584 >         * to be directed at us. We simply check to see
585 >         * if it starts with our nick in some fashion.
586 >         * This will return null if the message is not
587 >         * for us - thus allowing this method to perform
588 >         * two tasks at once.
589           *
590 <         * @param line the line to strip
591 <         * @return the message from the line
590 >         * @param message the message to check
591 >         * @return the message (with our nick removed), or null if it's not for us.
592           */
593 <        private String getMsg(String line) {
594 <            String result = "";
595 <            if(line.indexOf("PRIVMSG")!=-1) {
596 <                int firstColon = line.indexOf(":");
597 <                if(firstColon != -1) {
598 <                    int secondColon = line.indexOf(":", firstColon+1);
599 <                    if(secondColon != -1) {
600 <                        result = line.substring(secondColon+1);
601 <                    }
602 <                }
593 >        private String isForMe(String message) {
594 >            // change to lower case to remove
595 >            // case ambiguities
596 >            String nick = _nickname.toLowerCase();
597 >            String msg = message.toLowerCase();
598 >            if(msg.startsWith(nick + ", ") ||
599 >               msg.startsWith(nick + ": ") ||
600 >               msg.startsWith(nick + " ")) {
601 >                // NOTE that the 1 here is hardcoded to be the length
602 >                // of the above 'extra' character (eg , or :)
603 >                return message.substring(nick.length()+1).trim();
604              }
605 <            return result;
605 >            else {
606 >                return null;
607 >            }
608          }
609          
610          /**
611 <         * Finds out the sender of the message
612 <         *
613 <         * @param line the line to look for a sender in
590 <         * @return the sender
611 >         * Sleep for a configurable amount of time and
612 >         * then return. This is used when reconnecting
613 >         * to the server or a channel.
614           */
615 <        private String getMsgSender(String line) {
616 <            String result = "";
617 <            int colon = line.indexOf(":");
618 <            int excl = line.indexOf("!");
619 <            if(colon!=-1 && excl!=-1) {
620 <                result = line.substring(colon+1, excl);
615 >        private void reconnectSleep() {
616 >            int delayTime = 0;
617 >            try {
618 >                delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay"));
619 >            } catch (NumberFormatException e) {
620 >                delayTime = DEFAULT_RECONNECT_DELAY;
621 >                _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds");
622 >            } catch (PropertyNotFoundException e) {
623 >                delayTime = DEFAULT_RECONNECT_DELAY;
624 >                _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds");
625              }
626 <            return result;
626 >            _logger.write(this.toString(), Logger.ERROR, "Waiting "+delayTime+" seconds for reconnect...");
627 >            try {
628 >                Thread.sleep(delayTime * 1000);
629 >            } catch (InterruptedException e) {}
630          }
631          
632          /**
633 <         * The socket connected to the server
633 >         * Returns the revision of the bot.
634 >         *
635 >         * @return the revision of this bot
636           */
637 <        private Socket _socket;
637 >        private String getRevision() {
638 >            return REVISION.substring(11, REVISION.length()-2);
639 >        }
640          
641          /**
642 <         * The writer
642 >         * Overrides the {@link java.lang.Object#toString() Object.toString()}
643 >         * method to provide clean logging (every class should have this).
644 >         *
645 >         * This uses the uk.org.iscream.cms.server.util.NameFormat class
646 >         * to format the toString()
647 >         *
648 >         * @return the name of this class and its CVS revision
649           */
650 <        private PrintWriter _socketOut;
650 >        public String toString() {
651 >            return FormatName.getName(
652 >                _name,
653 >                getClass().getName(),
654 >                REVISION);
655 >        }
656          
657          /**
613         * The reader
614         */
615        private BufferedReader _socketIn;
616        
617        /**
658           * Just a reminder to what channel we're on...
659           * this can't be dynamic :)
660           */
# Line 624 | Line 664 | public class IRC__Alerter extends AlerterSkeleton {
664           * A reminder of our current nickname...
665           */
666          private String _nickname;
627        
628        /**
629         * This holds a reference to the
630         * system logger that is being used.
631         */
632        protected Logger _logger = ReferenceManager.getInstance().getLogger();
667          
668      }
669  

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines