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.1 by tdb, Thu Mar 1 23:45:45 2001 UTC vs.
Revision 1.14 by ajm, Mon Mar 5 12:09:28 2001 UTC

# Line 9 | Line 9 | import uk.ac.ukc.iscream.componentmanager.*;
9  
10   import java.io.*;
11   import java.net.*;
12 + import java.util.*;
13 + import java.text.*;
14  
15   /**
16   * This Alert sends an IRC message.
17   *
18 + * Clean shutdown could be achieved by stopping the run() method in the
19 + * IRCBot inner class.
20 + *
21   * @author  $Author$
22   * @version $Id$
23   */
# Line 25 | Line 30 | public class IRC__Alerter implements PluginAlerter {
30       */
31      public final String REVISION = "$Revision$";
32      
33 +    /**
34 +     * A description of this alerter
35 +     */
36      public final String DESC = "Sends alerts on an IRC channel";
37      
38 +    /**
39 +     * The default reconnect delay in seconds
40 +     */
41 +    public final int DEFAULT_RECONNECT_DELAY = 30;
42 +    
43   //---STATIC METHODS---
44  
45   //---CONSTRUCTORS---
46  
47      public IRC__Alerter() {
48 <        // get the configuration for this alerter
36 <        Configuration config = ReferenceManager.getInstance().getCM().getConfiguration(_name);
37 <              
38 <        // an integer value
39 <        _level = Integer.parseInt(config.getProperty("Alerter.IRC.level"));
40 <        // the hostname of the IRC server
41 <        _IRCServer = config.getProperty("Alerter.IRC.IRCServer");
42 <        // the port number of the IRC server
43 <        _IRCPort = Integer.parseInt(config.getProperty("Alerter.IRC.IRCPort"));
44 <        // the nickname to use
45 <        _nick = config.getProperty("Alerter.IRC.nick");
46 <        // the channel to join
47 <        _channel = config.getProperty("Alerter.IRC.channel");
48 <        // a message with the following: %level% and %message%
49 <        _message = config.getProperty("Alerter.IRC.message");
50 <        
48 >                        
49          // connect to the IRC server
50 <        _ircbot = null;
51 <        try {
52 <            _ircbot = new IRCBot();
53 <            _ircbot.connect();
54 <            _ircbot.sendNotice("iscreamBot activated");
57 <        } catch(IOException e) {
58 <            _logger.write(toString(), Logger.ERROR, "Error starting IRCBot: "+e);
59 <        }
50 >        _ircbot = new IRCBot();
51 >        _ircbot.start();
52 >        _startTime = System.currentTimeMillis();
53 >        
54 >        _logger.write(toString(), Logger.SYSINIT, "IRC Alerter started");
55      }
56  
57   //---PUBLIC METHODS---
58  
59      public void sendAlert(Alert alert) {
60 <        // only send if it's equal (or above) our level
61 <        if(alert.getLevel() >= _level) {
62 <            // sort out the message
63 <            String message = _message;
64 <            message = StringUtils.replaceText(message, "%level%", String.valueOf(alert.getLevel()));
65 <            message = StringUtils.replaceText(message, "%message%", alert.getMessage());
66 <            
67 <            // send the message
68 <            _ircbot.sendMsg(message);
60 >        // only send alerts if we're active
61 >        if(_active) {
62 >            ConfigurationProxy cp = ConfigurationProxy.getInstance();
63 >            String levelName = cp.getProperty(_name, "Alerter.IRC.level");
64 >            int level = StringUtils.getStringPos(levelName, Alert.alertLevels);
65 >            // only send if it's equal (or above) our level
66 >            if((alert.getLevel() == 0) || (alert.getLevel() >= level)) {
67 >                String alertType = Alert.alertLevels[alert.getLevel()];
68 >                String thresholdType = Alert.thresholdLevels[alert.getThreshold()];
69 >                // sort out the message
70 >                String message = cp.getProperty(_name, "Alerter.IRC.message");
71 >                message = StringUtils.replaceText(message, "%level%", alertType);
72 >                message = StringUtils.replaceText(message, "%threshold%", thresholdType);
73 >                message = StringUtils.replaceText(message, "%source%", alert.getSource());
74 >                message = StringUtils.replaceText(message, "%value%", alert.getValue());
75 >                message = StringUtils.replaceText(message, "%thresholdValue%", alert.getThresholdValue());
76 >                message = StringUtils.replaceText(message, "%attributeName%", alert.getAttributeName());
77 >                message = StringUtils.replaceText(message, "%timeTillNextAlert%",  getTimeString(Long.parseLong(alert.getTimeTillNextAlert())));
78 >                
79 >                // send the message
80 >                _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level");
81 >                _ircbot.sendMsg(message);
82 >                _lastAlert = message;
83 >                _lastAlertTime = System.currentTimeMillis();
84 >                _alertCount ++;
85 >            }
86          }
87      }
88  
# Line 99 | Line 111 | public class IRC__Alerter implements PluginAlerter {
111  
112   //---PRIVATE METHODS---
113  
114 +    private String getTimeString(long time) {
115 +        String timeString = null;
116 +        if (time >= 60) {
117 +            timeString = (time / 60) + " minute(s)";
118 +        } else if (time >= 3600) {
119 +            timeString = ((time/60) / 60) + " hour(s)";
120 +        } else {
121 +            timeString = time + " second(s)";
122 +        }
123 +        return timeString;
124 +    }
125 +
126   //---ACCESSOR/MUTATOR METHODS---
127  
128   //---ATTRIBUTES---
105
106    // an integer value
107    private int _level;
129      
130 <    // the hostname of the IRC server
131 <    private String _IRCServer;
130 >    /**
131 >     * A reference to the IRCBot
132 >     */
133 >    private IRCBot _ircbot;
134      
135 <    // the port number of the IRC server
136 <    private int _IRCPort;
135 >    /**
136 >     * Are we "active"
137 >     */
138 >    private boolean _active = false;
139      
140 <    // the nickname to use
141 <    private String _nick;
140 >    /**
141 >     * The last alert that was sent
142 >     */
143 >    private String _lastAlert = "no alerts have been sent";
144      
145 <    // the channel to join
146 <    private String _channel;
145 >    /**
146 >     * The time of the last alert
147 >     */
148 >    private long _lastAlertTime = -1;
149      
150 <    // the ircbot
151 <    private IRCBot _ircbot;
150 >    /**
151 >     * Number of alerts sent
152 >     */
153 >    private long _alertCount = 0;
154      
155 <    // a message with the following: %level% and %message%
156 <    private String _message;
155 >    /**
156 >     * Time of IRCBot startup
157 >     */
158 >    private long _startTime;
159      
160      /**
161       * This is the friendly identifier of the
# Line 133 | Line 166 | public class IRC__Alerter implements PluginAlerter {
166       * can be placed here.  This name could also
167       * be changed to null for utility classes.
168       */
169 <    private String _name = ClientMain.NAME;
169 >    private String _name = "IRC Alert";
170  
171      /**
172       * This holds a reference to the
# Line 159 | Line 192 | public class IRC__Alerter implements PluginAlerter {
192           * present moment it only responds to pings.
193           */
194          public void run() {
195 <            // flag so we can stop the loop
195 >            // so we can stop if required
196              boolean run = true;
197              while(run) {
198 +                // flag so we can stop the loop
199 +                boolean doRead = true;
200 +                // connect to the IRC server
201                  try {
202 <                    // read a command
203 <                    String cmd = _socketIn.readLine();
204 <                    // if it's a PING...
205 <                    if(cmd.startsWith("PING")) {
206 <                        // ...send a PONG
207 <                        _socketOut.println("PONG" + cmd.substring(4));
202 >                    connect();
203 >                    sendNotice(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice"));
204 >                } catch(IOException e) {
205 >                    doRead=false;
206 >                    _logger.write(this.toString(), Logger.ERROR, "Error connecting: "+e);
207 >                }
208 >                while(doRead) {
209 >                    try {
210 >                        // read a command
211 >                        String cmd = _socketIn.readLine();
212 >                        // if we have a null, we've lost contact
213 >                        if(cmd == null) {
214 >                            throw new IOException("End of stream reached");
215 >                        }
216 >                        // let another method deal with the input
217 >                        handleInput(cmd);
218 >                    } catch (IOException e) {
219 >                        // comms failure, maybe our link is dead.
220 >                        _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e);
221 >                        // stop, and loop round for a reconnect.
222 >                        doRead = false;
223                      }
224 +                }
225 +                // make sure we're disconnected
226 +                try {
227 +                    disconnect();
228                  } catch (IOException e) {
229 <                    // comms failure, lets stop
175 <                    _logger.write(this.toString(), Logger.ERROR, "Comms error: "+e);
176 <                    //run = false;
229 >                    _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e);
230                  }
231 +                
232 +                // comms have failed, so wait a while and reconnect
233 +                int delayTime = 0;
234 +                try {
235 +                    delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay"));
236 +                } catch (NumberFormatException e) {
237 +                    delayTime = DEFAULT_RECONNECT_DELAY;
238 +                    _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds");
239 +                } catch (org.omg.CORBA.MARSHAL e2) {
240 +                    delayTime = DEFAULT_RECONNECT_DELAY;
241 +                    _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds");
242 +                }
243 +                try {
244 +                    Thread.sleep(delayTime * 1000);
245 +                } catch (InterruptedException e) {}
246              }
247 +            // maybe disconnect here ? - shutdown method not implemented yet
248 +            //disconnect();
249          }
250          
251          /**
# Line 185 | Line 255 | public class IRC__Alerter implements PluginAlerter {
255           */
256          public void sendMsg(String msg) {
257              _socketOut.println("PRIVMSG "+_channel+" :"+msg);
258 +            // wait a second before returning...
259 +            // this ensures messages can't be sent too fast
260 +            try {Thread.sleep(1000);} catch (InterruptedException e) {}
261          }
262          
263          /**
264 +         * Sends a message to the channel.
265 +         *
266 +         * @param user The user to send to
267 +         * @param msg The message to send
268 +         */
269 +        public void sendPrivMsg(String user, String msg) {
270 +            _socketOut.println("PRIVMSG "+user+" :"+msg);
271 +            // wait a second before returning...
272 +            // this ensures messages can't be sent too fast
273 +            try {Thread.sleep(1000);} catch (InterruptedException e) {}
274 +        }
275 +        
276 +        /**
277 +         * Sends an action to the channel.
278 +         *
279 +         * @param msg the action message
280 +         */
281 +        public void sendAction(String msg) {
282 +            char esc = 001;
283 +            sendMsg(esc+"ACTION "+msg+esc);
284 +            // wait a second before returning...
285 +            // this ensures messages can't be sent too fast
286 +            try {Thread.sleep(1000);} catch (InterruptedException e) {}
287 +        }
288 +        
289 +        /**
290           * Sends a notice to the channel.
291           *
292           * @param msg The notice to send
293           */
294          public void sendNotice(String msg) {
295              _socketOut.println("NOTICE "+_channel+" :"+msg);
296 +            // wait a second before returning...
297 +            // this ensures messages can't be sent too fast
298 +            try {Thread.sleep(1000);} catch (InterruptedException e) {}
299          }
300          
301          /**
# Line 202 | Line 304 | public class IRC__Alerter implements PluginAlerter {
304           * @throws IOException if the connection fails
305           */
306          public void connect() throws IOException {
307 +            ConfigurationProxy cp = ConfigurationProxy.getInstance();
308              // setup the socket, reader and writer
309 <            _socket = new Socket(_IRCServer, _IRCPort);
309 >            String server = cp.getProperty(_name, "Alerter.IRC.IRCServer");
310 >            int port = Integer.parseInt(cp.getProperty(_name, "Alerter.IRC.IRCPort"));
311 >            _socket = new Socket(server, port);
312              _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
313              _socketOut = new PrintWriter(_socket.getOutputStream(), true);
314 <            // send the various log in messages
315 <            _socketOut.println("PASS");
316 <            _socketOut.println("NICK "+_nick);  
317 <            _socketOut.println("USER iscream 8 * :i-scream Bot");
314 >            //_socketOut.println("PASS");
315 >            // send USER details
316 >            String user = cp.getProperty(_name, "Alerter.IRC.user");
317 >            String comment = cp.getProperty(_name, "Alerter.IRC.comment");
318 >            _socketOut.println("USER "+user+" 8 * :"+comment);
319 >            // attempt to get a nick
320 >            String nickList = cp.getProperty(_name, "Alerter.IRC.nickList");
321 >            StringTokenizer st = new StringTokenizer(nickList, ";");
322 >            boolean ok = false;
323 >            // try until we exhaust our list
324 >            while(!ok && st.hasMoreTokens()) {
325 >                String nick = st.nextToken();
326 >                _socketOut.println("NICK "+nick);
327 >                // get a "yes" or "no" response back
328 >                String response = "";
329 >                do {
330 >                    response = _socketIn.readLine();
331 >                    if(response==null) {
332 >                        throw new IOException("Communication error whilst logging in");
333 >                    }
334 >                } while(response.indexOf("001")==-1 && response.indexOf("433")==-1);
335 >                // see if it was a yes
336 >                if(response.indexOf("001")!=-1) {
337 >                    // great, we're logged in !
338 >                    ok = true;
339 >                    // store the name we're using
340 >                    _nickname = nick;
341 >                }
342 >                else {
343 >                    // log we couldn't get the name
344 >                    _logger.write(this.toString(), Logger.WARNING, "Nickname in use: "+nick);
345 >                }
346 >            }
347 >            if(!ok) {
348 >                // oh dear, we couldn't get on.
349 >                throw new IOException("All nicknames in use");
350 >            }
351              // join the channel
352 +            _channel = cp.getProperty(_name, "Alerter.IRC.channel");
353              _socketOut.println("JOIN "+_channel);
354 <            // start our listening thread
355 <            this.start();
354 >            // allow alerts
355 >            _active = true;
356          }
357          
358          /**
# Line 222 | Line 361 | public class IRC__Alerter implements PluginAlerter {
361           * @throws IOException if the disconnection fails
362           */
363          public void disconnect() throws IOException {
364 <            // send proper QUIET
364 >            // stop alerts
365 >            _active = false;
366 >            // send proper QUIT
367              _socketOut.println("QUIT : iscreamBot component shutting down...");
368              // close the socket
369              _socketOut.close();
370              _socketIn.close();
371              _socket.close();
372          }
373 <                        
373 >        
374          /**
375 +         * Overrides the {@link java.lang.Object#toString() Object.toString()}
376 +         * method to provide clean logging (every class should have this).
377 +         *
378 +         * This uses the uk.ac.ukc.iscream.util.NameFormat class
379 +         * to format the toString()
380 +         *
381 +         * @return the name of this class and its CVS revision
382 +         */
383 +        public String toString() {
384 +            return FormatName.getName(
385 +                _name,
386 +                getClass().getName(),
387 +                REVISION);
388 +        }
389 +        
390 +        /**
391 +         * Deals with incoming lines from the server.
392 +         *
393 +         * @param line the line to deal with
394 +         */
395 +        private void handleInput(String line) {
396 +            ConfigurationProxy cp = ConfigurationProxy.getInstance();
397 +            // if it's a PING...
398 +            if(line.startsWith("PING")) {
399 +                // ...send a PONG
400 +                _socketOut.println("PONG" + line.substring(4));
401 +            }
402 +            // see if it's for us
403 +            else if(getMsg(line).startsWith(_nickname+",")
404 +                    || getMsg(line).startsWith(_nickname+":")
405 +                    || getMsg(line).startsWith(_nickname+" ")) {
406 +                // we have a message for us
407 +                String message = getMsg(line).substring(_nickname.length());
408 +                if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.stopCommand"))!=-1) {
409 +                    _active = false;
410 +                    sendMsg(getMsgSender(line)+", alerts have been stopped");
411 +                }
412 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.startCommand"))!=-1) {
413 +                    _active = true;
414 +                    sendMsg(getMsgSender(line)+", alerts have been activated");
415 +                }
416 +                // this needs to go here if it contains the same words as the lastAlertCommand
417 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand"))!=-1) {
418 +                    if(_lastAlertTime != -1) {
419 +                        long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
420 +                        String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
421 +                        sendMsg(getMsgSender(line)+", I last sent an alert "+uptimeText+ " ago");
422 +                    }
423 +                    else {
424 +                        sendMsg(getMsgSender(line)+", I've never sent an alert!");
425 +                    }
426 +                }
427 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.lastAlertCommand"))!=-1) {
428 +                    if(_lastAlertTime != -1) {
429 +                        String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
430 +                        sendMsg(getMsgSender(line)+", last alert was at "+date+"; "+_lastAlert);
431 +                    }
432 +                    else {
433 +                        sendMsg(getMsgSender(line)+", I've never sent an alert!");
434 +                    }
435 +                    
436 +                }
437 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.joinCommand"))!=-1) {
438 +                    String joinCmd = cp.getProperty(_name, "Alerter.IRC.joinCommand");
439 +                    String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
440 +                    int endOfChan = newChan.indexOf(" ");
441 +                    if(endOfChan == -1) {
442 +                        endOfChan = newChan.length();
443 +                    }
444 +                    newChan = newChan.substring(0, endOfChan);
445 +                    sendMsg(getMsgSender(line)+", okay, I'm off to "+newChan);
446 +                    _socketOut.println("PART "+_channel);
447 +                    _socketOut.println("JOIN "+newChan);
448 +                    _channel = newChan;
449 +                }
450 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.nickChangeCommand"))!=-1) {
451 +                    String nickChangeCmd = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand");
452 +                    String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
453 +                    int endOfNick = newNick.indexOf(" ");
454 +                    if(endOfNick == -1) {
455 +                        endOfNick = newNick.length();
456 +                    }
457 +                    newNick = newNick.substring(0, endOfNick);
458 +                    sendMsg(getMsgSender(line)+", okay, changing my nickname to "+newNick);
459 +                    _socketOut.println("NICK "+newNick);
460 +                    _nickname = newNick;
461 +                }
462 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.versionCommand"))!=-1) {
463 +                    sendMsg(getMsgSender(line)+", I am version "+REVISION.substring(11, REVISION.length() -2)+" of the i-scream alerting bot");
464 +                }
465 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.helpCommand"))!=-1) {
466 +                    sendPrivMsg(getMsgSender(line), "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
467 +                    sendPrivMsg(getMsgSender(line), "I understand the following commands;");
468 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.stopCommand"));
469 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.startCommand"));
470 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.lastAlertCommand"));
471 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.joinCommand"));
472 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.nickChangeCommand"));
473 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.statCommand"));
474 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.uptimeCommand"));
475 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand"));
476 +                    sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.helpCommand"));
477 +                }
478 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.statCommand"))!=-1) {
479 +                    sendMsg(getMsgSender(line)+", I have sent a total of "+_alertCount+" alerts!");
480 +                }
481 +                else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.uptimeCommand"))!=-1) {
482 +                    long uptime = (System.currentTimeMillis() - _startTime) / 1000;
483 +                    String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
484 +                    sendMsg(getMsgSender(line)+", I have been running for "+uptimeText);
485 +                }
486 +                else if(message.indexOf("do a jibble dance")!=-1) {
487 +                    // little joke :)
488 +                    sendAction("jives to the funky beat shouting \"ii--screeeaaammm\"");
489 +                }
490 +                else {
491 +                    sendMsg(getMsgSender(line)+", "+cp.getProperty(_name, "Alerter.IRC.rejectMessage"));
492 +                }
493 +            }
494 +            else if(line.indexOf(_nickname)!=-1 && line.indexOf(_channel)!=-1 && line.indexOf("KICK")!=-1) {
495 +                sendPrivMsg(getMsgSender(line), "That wasn't a nice thing to do...");
496 +                _channel = cp.getProperty(_name, "Alerter.IRC.channel");
497 +                _socketOut.println("JOIN "+_channel);
498 +            }
499 +        }
500 +        
501 +        /**
502 +         * Strips the header from a message line
503 +         *
504 +         * @param line the line to strip
505 +         * @return the message from the line
506 +         */
507 +        private String getMsg(String line) {
508 +            String result = "";
509 +            if(line.indexOf("PRIVMSG")!=-1) {
510 +                int firstColon = line.indexOf(":");
511 +                if(firstColon != -1) {
512 +                    int secondColon = line.indexOf(":", firstColon+1);
513 +                    if(secondColon != -1) {
514 +                        result = line.substring(secondColon+1);
515 +                    }
516 +                }
517 +            }
518 +            return result;
519 +        }
520 +        
521 +        /**
522 +         * Finds out the sender of the message
523 +         *
524 +         * @param line the line to look for a sender in
525 +         * @return the sender
526 +         */
527 +        private String getMsgSender(String line) {
528 +            String result = "";
529 +            int colon = line.indexOf(":");
530 +            int excl = line.indexOf("!");
531 +            if(colon!=-1 && excl!=-1) {
532 +                result = line.substring(colon+1, excl);
533 +            }
534 +            return result;
535 +        }
536 +        
537 +        /**
538           * The socket connected to the server
539           */
540          private Socket _socket;
# Line 244 | Line 548 | public class IRC__Alerter implements PluginAlerter {
548           * The reader
549           */
550          private BufferedReader _socketIn;
551 +        
552 +        /**
553 +         * Just a reminder to what channel we're on...
554 +         * this can't be dynamic :)
555 +         */
556 +        private String _channel;
557 +        
558 +        /**
559 +         * A reminder of our current nickname...
560 +         */
561 +        private String _nickname;
562          
563      }
564  

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines