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.9 by tdb, Sun Mar 4 03:34:50 2001 UTC vs.
Revision 1.30.2.3 by tdb, Tue Feb 5 18:00:15 2002 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines