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.30.2.2 by tdb, Mon Feb 4 23:14:38 2002 UTC vs.
Revision 1.35 by tdb, Wed Feb 5 16:43:45 2003 UTC

# Line 1 | Line 1
1 + /*
2 + * i-scream central monitoring system
3 + * http://www.i-scream.org.uk
4 + * Copyright (C) 2000-2002 i-scream
5 + *
6 + * This program is free software; you can redistribute it and/or
7 + * modify it under the terms of the GNU General Public License
8 + * as published by the Free Software Foundation; either version 2
9 + * of the License, or (at your option) any later version.
10 + *
11 + * This program is distributed in the hope that it will be useful,
12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 + * GNU General Public License for more details.
15 + *
16 + * You should have received a copy of the GNU General Public License
17 + * along with this program; if not, write to the Free Software
18 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 + */
20 +
21   //---PACKAGE DECLARATION---
22   package uk.org.iscream.cms.server.client.alerters;
23  
24   //---IMPORTS---
25   import uk.org.iscream.cms.server.client.*;
26   import uk.org.iscream.cms.server.core.*;
27 < import uk.org.iscream.cms.server.util.*;
27 > import uk.org.iscream.cms.util.*;
28   import uk.org.iscream.cms.server.componentmanager.*;
29   import java.io.*;
30   import java.net.*;
# Line 43 | Line 63 | public class IRC__Alerter extends AlerterSkeleton {
63          super();
64          // construct and initialise the bot
65          _ircbot = new IRCBot();
66 <        //_ircbot.setVerbose(true);
66 >        _ircbot.setVerbose(false);
67          Thread ircThread = new Thread(_ircbot);
68          // set it's name and start it
69          ircThread.setName("client.IRC__Alerter$IRCBot");
# Line 77 | Line 97 | public class IRC__Alerter extends AlerterSkeleton {
97          if(_active) {          
98              // send the message
99              _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level");
100 <            _ircbot.sendMsg(message);
100 >            _ircbot.sendMessage(message);
101              // count sent alerts
102              _alertCount++;
103          } else {
# Line 94 | Line 114 | public class IRC__Alerter extends AlerterSkeleton {
114       * Overrides the {@link java.lang.Object#toString() Object.toString()}
115       * method to provide clean logging (every class should have this).
116       *
117 <     * This uses the uk.org.iscream.cms.server.util.NameFormat class
117 >     * This uses the uk.org.iscream.cms.util.NameFormat class
118       * to format the toString()
119       *
120       * @return the name of this class and its CVS revision
# Line 195 | Line 215 | public class IRC__Alerter extends AlerterSkeleton {
215           */
216          public final int DEFAULT_RECONNECT_DELAY = 30;
217          
218 +        /**
219 +         * Attempt to kick-start the IRCBot into action. Will
220 +         * keep calling init() until it doesn't throw an
221 +         * an IOException (which will only be thrown if there
222 +         * is an error initialising). After a successful call
223 +         * to init() the method finishes.
224 +         */
225          public void run() {
226              while(true) {
227                  try {
# Line 203 | Line 230 | public class IRC__Alerter extends AlerterSkeleton {
230                  }
231                  catch (IOException e) {
232                      _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e);
233 +                    // wait for a while, defined in the config
234                      reconnectSleep();
235                  }
236              }
209            //System.out.println("falling out!");
237          }
238          
239 <        public void init() throws IOException {
239 >        /**
240 >         * Connects the bot to an irc server and joins a channel,
241 >         * using details supplied from the i-scream configuration
242 >         * system. If this method completes without an exception
243 >         * one can presume the bot is ready for use.
244 >         *
245 >         * @throws IOException if there is any problem initialising
246 >         */
247 >        private void init() throws IOException {
248              _logger.write(this.toString(), Logger.DEBUG, "Initialising IRCBot...");
249 +            
250 +            // get a hook on the configuration system
251              ConfigurationProxy cp = ConfigurationProxy.getInstance();
252 +            
253 +            // get hold of the server details
254              String server;
255              int port;
256              try {
# Line 224 | Line 263 | public class IRC__Alerter extends AlerterSkeleton {
263                  _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
264                  throw new IOException("Can't get irc server details due to malformed configuration");
265              }
266 <            String user, comment;
266 >            
267 >            // get hold of the user details and nickname list
268 >            String user, nickList;
269              try {
270                  user = cp.getProperty(_name, "Alerter.IRC.user");
271 <                comment = cp.getProperty(_name, "Alerter.IRC.comment");
271 >                nickList = cp.getProperty(_name, "Alerter.IRC.nickList");
272              } catch (PropertyNotFoundException e) {
273                  _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
274 <                throw new IOException("Can't get user details due to configuration error");
274 >                throw new IOException("Can't get user/nickname details due to configuration error");
275              }
276 <            this.setLogin(user);
277 <            this.setVersion(comment);
278 <            this.setFinger(comment); // ?
279 <            String nickList;
276 >            
277 >            // get hold of comment and finger information
278 >            //  -- we're not too fussed if these aren't set
279 >            String comment = "Alerter.IRC.comment is undefined";
280 >            String finger = "Alerter.IRC.finger is undefined";
281              try {
282 <                nickList = cp.getProperty(_name, "Alerter.IRC.nickList");
282 >                comment = cp.getProperty(_name, "Alerter.IRC.comment");
283 >                finger = cp.getProperty(_name, "Alerter.IRC.finger");
284              } catch (PropertyNotFoundException e) {
285 <                _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
243 <                throw new IOException("Can't get nickname due to configuration error");
285 >                _logger.write(this.toString(), Logger.WARNING, "Configuration warning, using default: "+e);
286              }
287 +            
288 +            // put these details into the bot
289 +            this.setLogin(user);
290 +            this.setVersion("[" + getRevision() + "] " + comment);
291 +            this.setFinger(finger);
292 +            
293 +            // attempt to connect, trying each nickname
294 +            // in turn until sucessfully connected
295              StringTokenizer st = new StringTokenizer(nickList, ";");
296              boolean ok = false;
297              while(!ok && st.hasMoreTokens()) {
298                  String nick = st.nextToken();
299                  try {
300 +                    // try to connect with a nickname
301                      _logger.write(this.toString(), Logger.DEBUG, "Trying nick: "+nick);
302                      this.setName(nick);
303                      this.connect(server, port);
304 <                    // must be ok if we get here
304 >                    // at this point we know the nickname was accepted
305                      _nickname = nick;
306                      ok = true;
307                  }
# Line 258 | Line 309 | public class IRC__Alerter extends AlerterSkeleton {
309                      _logger.write(this.toString(), Logger.ERROR, "IO error when connecting to server: "+e);
310                      throw new IOException("IO error when connecting to server");
311                  }
312 +                catch(NickAlreadyInUseException e) {
313 +                    _logger.write(this.toString(), Logger.ERROR, "Nickname "+nick+" is already in use: "+e);
314 +                    // don't do anything, instead just loop round
315 +                    // and try the next nickname in the list
316 +                }
317                  catch(IrcException e) {
318                      _logger.write(this.toString(), Logger.ERROR, "IRC error when connecting to server: "+e);
319                      throw new IOException("IRC error when connecting to server");
320                  }
265                catch(NickAlreadyInUseException e) {
266                    _logger.write(this.toString(), Logger.ERROR, "Nickname "+nick+" is already in use: "+e);
267                    // just carry on around while loop
268                }
321              }
322              if(!ok) {
323 <                // oh dear, we couldn't get on.
323 >                // must have tried all the nicknames, best bail out
324                  _logger.write(this.toString(), Logger.ERROR, "All nicknames already in use");
325                  throw new IOException("All nicknames already in use");
326              }
327 +            
328 +            // get hold of the channel details, and attempt
329 +            // to join the specified channel
330              try {
331                  _channel = cp.getProperty(_name, "Alerter.IRC.channel");
332                  this.joinChannel(_channel);
# Line 280 | Line 335 | public class IRC__Alerter extends AlerterSkeleton {
335                  throw new IOException("Can't get channel name due to configuration error");
336              }
337              
338 +            // get hold of the startup notice, and send
339 +            // it if it's defined
340              String startupNotice;
341              try {
342                  startupNotice = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice");
# Line 288 | Line 345 | public class IRC__Alerter extends AlerterSkeleton {
345                  _logger.write(this.toString(), Logger.DEBUG, "Startup notice not defined, so not sending: "+e);
346              }
347              
348 <            // we should set this when all is ready!
348 >            // at this point initialisation is complete, so
349 >            // we can return and set this flag to allow alerts
350              _active = true;
293            //System.out.println("leaving init");
351          }
352          
353 <        public void sendMsg(String msg) {
354 <            sendMessage(_channel, msg);
353 >        /**
354 >         * Send a message to the current channel.
355 >         *
356 >         * @param message The message to send
357 >         */
358 >        private void sendMessage(String message) {
359 >            sendMessage(_channel, message);
360          }
361          
362 +        /**
363 +         * If we get disconnected this method will be
364 +         * called, so we must take action. We'll simply
365 +         * keep trying to reinitialise, and thus
366 +         * reconnect to the server.
367 +         */
368          public void onDisconnect() {
369 +            // stop alerts being sent for now
370              _active = false;
371              while(true) {
372 +                // wait for a while, defined in the config
373                  reconnectSleep();
374                  try {
375                      init();
# Line 309 | Line 379 | public class IRC__Alerter extends AlerterSkeleton {
379                      _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e);
380                  }
381              }
312            //System.out.println("falling out of disconnect!");
382          }
383          
384 +        /**
385 +         * If we receive a message this method will
386 +         * be called. We'll check if the message is
387 +         * for us, and call handleInput if it is.
388 +         *
389 +         * @param channel The channel the message came from
390 +         * @param sender The sender of the message
391 +         * @param login The login of the sender
392 +         * @param hostname The hostname of the sender
393 +         * @param message The message sent
394 +         */
395          public void onMessage(String channel, String sender, String login, String hostname, String message) {
396 <            if(isForMe(message)) {
397 <                handleInput(message, channel);
396 >            String trimmedMessage = isForMe(message);
397 >            // if trimmedMessage is null, it's not for us
398 >            if(trimmedMessage != null) {
399 >                handleInput(trimmedMessage, channel);
400              }
401          }
402          
403 +        /**
404 +         * If we receive a private message this method
405 +         * will be called. No need to check if it's for
406 +         * us -- it has to be if it's a private message.
407 +         *
408 +         * @param sender The sender of the message
409 +         * @param login The login of the sender
410 +         * @param hostname The hostname of the sender
411 +         * @param message The message sent
412 +         */
413          public void onPrivateMessage(String sender, String login, String hostname, String message) {
414              handleInput(message, sender);
415          }
416          
417 +        /**
418 +         * If we receive a nick change message, this
419 +         * method gets called. We don't care about
420 +         * other users changing their nick, so we only
421 +         * take notice if our nick changes. If it does
422 +         * we need to change our internal nickname
423 +         * state.
424 +         *
425 +         * @param oldNick the old nickname
426 +         * @param login the login of the nick changer
427 +         * @param hostname the hostname of the nick changer
428 +         * @param newNick the new nickname
429 +         */
430          public void onNickChange(String oldNick, String login, String hostname, String newNick) {
431              if(oldNick.equals(_nickname)) {
432                  _nickname = newNick;
433 +                // tell the underlying pircbot that our nick has changed too :)
434 +                setName(newNick);
435              }
436          }
437          
438 +        /**
439 +         * If we receive a kick message this method
440 +         * gets called. We only take notice if it's
441 +         * us getting kicked, and then we'll rejoin
442 +         * the channel after a short wait.
443 +         *
444 +         * @param channel the channel the kick happened on
445 +         * @param kickerNick the person who performed the kick
446 +         * @param kickerLogin the login of the person who performed the kick
447 +         * @param kickerHostname the hostname of the person who performed the kick
448 +         * @param recipientNick the nickname of the person being kicked
449 +         * @param reason the reason for the kick
450 +         */
451          public void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason) {
452              if(recipientNick.equals(_nickname) && channel.equals(_channel)) {
453 +                // remind the person it's not very nice :)
454                  sendMessage(kickerNick, "That wasn't a nice thing to do...");
455 +                // get hold of the channel details, in case they've changed
456                  try {
457                      _channel = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.channel");
458                  } catch(PropertyNotFoundException e) {
459 <                    _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
459 >                    _logger.write(this.toString(), Logger.ERROR, "Can't get channel name due to configuration error: "+e);
460                  }
461 <                // should this be in the try?
461 >                // wait for a while, defined in the config
462 >                reconnectSleep();
463 >                // we'll try and rejoin the channel regardless
464 >                // otherwise we might end up doing nothing!
465                  joinChannel(_channel);
466              }
467          }
468          
469 +        /**
470 +         * This method handles input directed to us, and
471 +         * responds accordingly if required.
472 +         *
473 +         * @param message the message from someone
474 +         * @param source where the message came from, so we know where to send the response
475 +         */
476          private void handleInput(String message, String source) {
477 <            System.out.println("message: "+message);
346 <            System.out.println("from: "+source);
477 >            // get hold of the configuration system
478              ConfigurationProxy cp = ConfigurationProxy.getInstance();
479              // setup some String's
480              String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand;
481              String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand;
482 <            // get the command set
482 >            // get the command set from the configuration
483              try {
484                  stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand");
485                  startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand");
# Line 367 | Line 498 | public class IRC__Alerter extends AlerterSkeleton {
498                  return;
499              }
500              
501 <            if(message.indexOf(stopCommand) != -1) {
501 >            if(message.equalsIgnoreCase(stopCommand)) {
502                  _active = false;
503                  sendMessage(source, "alerts have been stopped");
504              }
505 <            else if(message.indexOf(startCommand) != -1) {
505 >            else if(message.equalsIgnoreCase(startCommand)) {
506                  _active = true;
507                  sendMessage(source, "alerts have been activated");
508              }
509 <            // this needs to go here if it contains the same words as the lastAlertCommand
510 <            else if(message.indexOf(timeSinceLastAlertCommand) != -1) {
509 >            // this needs to go before lastAlertCommand if it contains
510 >            // the same words as the lastAlertCommand.
511 >            else if(message.equalsIgnoreCase(timeSinceLastAlertCommand)) {
512                  if(_lastAlertTime != -1) {
513                      long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
514                      String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
# Line 386 | Line 518 | public class IRC__Alerter extends AlerterSkeleton {
518                      sendMessage(source, "I've never sent an alert!");
519                  }
520              }
521 <            else if(message.indexOf(lastAlertCommand) != -1) {
521 >            else if(message.equalsIgnoreCase(lastAlertCommand)) {
522                  if(_lastAlertTime != -1) {
523                      String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
524                      sendMessage(source, "last alert was at "+date+"; "+_lastAlert);
# Line 396 | Line 528 | public class IRC__Alerter extends AlerterSkeleton {
528                  }
529                  
530              }
531 <            else if(message.indexOf(joinCommand) != -1) {
531 >            else if(message.toLowerCase().startsWith(joinCommand.toLowerCase())) {
532                  String joinCmd = joinCommand;
533                  String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
534                  int endOfChan = newChan.indexOf(" ");
# Line 410 | Line 542 | public class IRC__Alerter extends AlerterSkeleton {
542                      partChannel(_channel);
543                      joinChannel(newChan);
544                      _channel = newChan;
413                    //return null; // ???
545                  }
546              }
547 <            else if(message.indexOf(nickChangeCommand) != -1) {
547 >            else if(message.toLowerCase().startsWith(nickChangeCommand.toLowerCase())) {
548                  String nickChangeCmd = nickChangeCommand;
549                  String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
550                  int endOfNick = newNick.indexOf(" ");
# Line 422 | Line 553 | public class IRC__Alerter extends AlerterSkeleton {
553                  }
554                  newNick = newNick.substring(0, endOfNick);
555                  changeNick(newNick);
425                // should we check this worked?
426                //_nickname = newNick;
427                //return null; // ???
556              }
557 <            else if(message.indexOf(versionCommand) != -1) {
558 <                sendMessage(source, "I am version "+REVISION.substring(11, REVISION.length() -2)+" of the i-scream alerting bot");
557 >            else if(message.equalsIgnoreCase(versionCommand)) {
558 >                sendMessage(source, "I am version " + getRevision() + " of the i-scream alerting bot");
559              }
560 <            else if(message.indexOf(helpCommand) != -1) {
433 <                //System.out.println("--starting help");
560 >            else if(message.equalsIgnoreCase(helpCommand)) {
561                  sendMessage(source, "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
562                  sendMessage(source, "I understand the following commands;");
563                  sendMessage(source, stopCommand);
# Line 442 | Line 569 | public class IRC__Alerter extends AlerterSkeleton {
569                  sendMessage(source, uptimeCommand);
570                  sendMessage(source, timeSinceLastAlertCommand);
571                  sendMessage(source, helpCommand);
445                //System.out.println("--ending help");
572              }
573 <            else if(message.indexOf(statCommand) != -1) {
573 >            else if(message.equalsIgnoreCase(statCommand)) {
574                  sendMessage(source, "I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!");
575              }
576 <            else if(message.indexOf(uptimeCommand) != -1) {
576 >            else if(message.equalsIgnoreCase(uptimeCommand)) {
577                  long uptime = (System.currentTimeMillis() - _startTime) / 1000;
578                  String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
579                  sendMessage(source, "I have been running for "+uptimeText);
580              }
581 <            else if(message.indexOf("ping") != -1) {
581 >            else if(message.equalsIgnoreCase("ping")) {
582                  sendMessage(source, "pong");
583              }
584 <            else if(message.indexOf("do a jibble dance") != -1) {
584 >            else if(message.equalsIgnoreCase("do a jibble dance")) {
585                  // little joke :)
586                  sendAction(source, "jives to the funky beat shouting \"ii--screeeaaammm\"");
587              }
462            
588              else {
589                  String rejectMessage = NOT_CONFIGURED;
590                  try {
# Line 471 | Line 596 | public class IRC__Alerter extends AlerterSkeleton {
596              }
597          }
598          
599 <        private boolean isForMe(String message) {
600 <            // change this!
599 >        /**
600 >         * Quick method to check if the message appears
601 >         * to be directed at us. We simply check to see
602 >         * if it starts with our nick in some fashion.
603 >         * This will return null if the message is not
604 >         * for us - thus allowing this method to perform
605 >         * two tasks at once.
606 >         *
607 >         * @param message the message to check
608 >         * @return the message (with our nick removed), or null if it's not for us.
609 >         */
610 >        private String isForMe(String message) {
611 >            // change to lower case to remove
612 >            // case ambiguities
613              String nick = _nickname.toLowerCase();
614              String msg = message.toLowerCase();
615              if(msg.startsWith(nick + ", ") ||
616                 msg.startsWith(nick + ": ") ||
617                 msg.startsWith(nick + " ")) {
618 <                return true;
618 >                // NOTE that the 1 here is hardcoded to be the length
619 >                // of the above 'extra' character (eg , or :)
620 >                return message.substring(nick.length()+1).trim();
621              }
622              else {
623 <                return false;
623 >                return null;
624              }
625          }
626          
627 +        /**
628 +         * Sleep for a configurable amount of time and
629 +         * then return. This is used when reconnecting
630 +         * to the server or a channel.
631 +         */
632          private void reconnectSleep() {
633              int delayTime = 0;
634              try {
# Line 503 | Line 647 | public class IRC__Alerter extends AlerterSkeleton {
647          }
648          
649          /**
650 +         * Returns the revision of the bot.
651 +         *
652 +         * @return the revision of this bot
653 +         */
654 +        private String getRevision() {
655 +            return REVISION.substring(11, REVISION.length()-2);
656 +        }
657 +        
658 +        /**
659           * Overrides the {@link java.lang.Object#toString() Object.toString()}
660           * method to provide clean logging (every class should have this).
661           *
662 <         * This uses the uk.org.iscream.cms.server.util.NameFormat class
662 >         * This uses the uk.org.iscream.cms.util.NameFormat class
663           * to format the toString()
664           *
665           * @return the name of this class and its CVS revision

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines