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
Revision: 1.21
Committed: Thu Mar 15 04:06:57 2001 UTC (23 years, 2 months ago) by tdb
Branch: MAIN
Changes since 1.20: +10 -6 lines
Log Message:
A quick little change so that it won't try and join a channel it's already in.

File Contents

# Content
1 //---PACKAGE DECLARATION---
2 package uk.org.iscream.client.alerters;
3
4 //---IMPORTS---
5 import uk.org.iscream.client.*;
6 import uk.org.iscream.core.*;
7 import uk.org.iscream.util.*;
8 import uk.org.iscream.componentmanager.*;
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: tdb1 $
22 * @version $Id: IRC__Alerter.java,v 1.20 2001/03/14 23:25:29 tdb1 Exp $
23 */
24 public class IRC__Alerter implements PluginAlerter {
25
26 //---FINAL ATTRIBUTES---
27
28 /**
29 * The current CVS revision of this class
30 */
31 public final String REVISION = "$Revision: 1.20 $";
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 public final String DEFAULT_LEVEL = Alert.alertLevels[0];
44
45 public final String NOT_CONFIGURED = "<NOT CONFIGURED>";
46
47 //---STATIC METHODS---
48
49 //---CONSTRUCTORS---
50
51 public IRC__Alerter() {
52
53 // connect to the IRC server
54 _ircbot = new IRCBot();
55 // set it's name and start it
56 _ircbot.setName("client.IRC__Alerter$IRCBot");
57 _ircbot.start();
58 _startTime = System.currentTimeMillis();
59
60 _logger.write(toString(), Logger.SYSINIT, "IRC Alerter started");
61 }
62
63 //---PUBLIC METHODS---
64
65 public void sendAlert(Alert alert) {
66 // only send alerts if we're active
67 if(_active) {
68 ConfigurationProxy cp = ConfigurationProxy.getInstance();
69
70 String levelName;
71 try {
72 levelName = cp.getProperty(_name, "Alerter.IRC.level");
73 } catch (PropertyNotFoundException e) {
74 levelName = DEFAULT_LEVEL;
75 _logger.write(toString(), Logger.WARNING, "Alerter.IRC.level value unavailable using default of " + levelName);
76 }
77 int level = StringUtils.getStringPos(levelName, Alert.alertLevels);
78 // only send if it's equal (or above) our level
79 if(((alert.getLevel() == 0) && (alert.getLastLevel() >= level)) || (alert.getLevel() >= level)) {
80 String alertType = Alert.alertLevels[alert.getLevel()];
81 String thresholdType = Alert.thresholdLevels[alert.getThreshold()];
82 String timeFirstSince = DateUtils.formatTime(System.currentTimeMillis() - alert.getInitialAlertTime(), "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
83 String timeFirstOccured = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(alert.getInitialAlertTime()));
84 // sort out the message
85 String message;
86 try {
87 message = cp.getProperty(_name, "Alerter.IRC.message");
88 } catch (PropertyNotFoundException e) {
89 message = NOT_CONFIGURED;
90 _logger.write(toString(), Logger.WARNING, "Alerter.IRC.message value unavailable using default of " + message);
91 }
92
93 message = StringUtils.replaceText(message, "%level%", alertType);
94 message = StringUtils.replaceText(message, "%threshold%", thresholdType);
95 message = StringUtils.replaceText(message, "%source%", alert.getSource());
96 message = StringUtils.replaceText(message, "%value%", alert.getValue());
97 message = StringUtils.replaceText(message, "%thresholdValue%", alert.getThresholdValue());
98 message = StringUtils.replaceText(message, "%attributeName%", alert.getAttributeName());
99 message = StringUtils.replaceText(message, "%timeTillNextAlert%", getTimeString(Long.parseLong(alert.getTimeTillNextAlert())));
100 message = StringUtils.replaceText(message, "%timeSinceFirstAlert%", timeFirstSince);
101 message = StringUtils.replaceText(message, "%timeOfFirstAlert%", timeFirstOccured);
102
103 // send the message
104 _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level");
105 _ircbot.sendMsg(message);
106 _lastAlert = message;
107 _lastAlertTime = System.currentTimeMillis();
108 _alertCount ++;
109 }
110 }
111 else {
112 _ignoredCount ++;
113 }
114 }
115
116 /**
117 * Overrides the {@link java.lang.Object#toString() Object.toString()}
118 * method to provide clean logging (every class should have this).
119 *
120 * This uses the uk.org.iscream.util.NameFormat class
121 * to format the toString()
122 *
123 * @return the name of this class and its CVS revision
124 */
125 public String toString() {
126 return FormatName.getName(
127 _name,
128 getClass().getName(),
129 REVISION);
130 }
131
132 /**
133 * return the String representation of what the filter does
134 */
135 public String getDescription(){
136 return DESC;
137 }
138
139 //---PRIVATE METHODS---
140
141 private String getTimeString(long time) {
142 String timeString = null;
143 if (time >= 60) {
144 timeString = (time / 60) + " minute(s)";
145 } else if (time >= 3600) {
146 timeString = ((time/60) / 60) + " hour(s)";
147 } else {
148 timeString = time + " second(s)";
149 }
150 return timeString;
151 }
152
153 //---ACCESSOR/MUTATOR METHODS---
154
155 //---ATTRIBUTES---
156
157 /**
158 * A reference to the IRCBot
159 */
160 private IRCBot _ircbot;
161
162 /**
163 * Are we "active"
164 */
165 private boolean _active = false;
166
167 /**
168 * The last alert that was sent
169 */
170 private String _lastAlert = "no alerts have been sent";
171
172 /**
173 * The time of the last alert
174 */
175 private long _lastAlertTime = -1;
176
177 /**
178 * Number of alerts sent
179 */
180 private int _alertCount = 0;
181
182 /**
183 * Number of alerts ignored when in "stopped" mode
184 */
185 private int _ignoredCount = 0;
186
187 /**
188 * Time of IRCBot startup
189 */
190 private long _startTime;
191
192 /**
193 * This is the friendly identifier of the
194 * component this class is running in.
195 * eg, a Filter may be called "filter1",
196 * If this class does not have an owning
197 * component, a name from the configuration
198 * can be placed here. This name could also
199 * be changed to null for utility classes.
200 */
201 private String _name = "IRC Alert";
202
203 /**
204 * This holds a reference to the
205 * system logger that is being used.
206 */
207 private Logger _logger = ReferenceManager.getInstance().getLogger();
208
209 //---STATIC ATTRIBUTES---
210
211 //---INNER CLASSES---
212
213 /**
214 * This class provides some basic IRCBot functionality. It connects
215 * to a specified server, and will remain there until told to
216 * leave. Whilst connected it can send a message or a notice to
217 * the server.
218 */
219 class IRCBot extends Thread {
220
221 public static final String DEFAULT_STARTUP_NOTICE = "i-scream ircbot starting...";
222
223 /**
224 * Main thread loop, this part of the class listens for
225 * messages from the server, and acts accordingly. At the
226 * present moment it only responds to pings.
227 */
228 public void run() {
229 // so we can stop if required
230 boolean run = true;
231 while(run) {
232 // flag so we can stop the loop
233 boolean doRead = true;
234 // get the startup notice
235 String startupNotice;
236 try {
237 startupNotice = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice");
238 } catch (PropertyNotFoundException e) {
239 startupNotice = DEFAULT_STARTUP_NOTICE;
240 _logger.write(this.toString(), Logger.WARNING, "Configuration error: "+e);
241 }
242 // connect to the IRC server
243 try {
244 connect();
245 sendNotice(startupNotice);
246 } catch(IOException e) {
247 doRead=false;
248 _logger.write(this.toString(), Logger.ERROR, "Error connecting: "+e);
249 }
250 while(doRead) {
251 try {
252 // read a command
253 String cmd = _socketIn.readLine();
254 // if we have a null, we've lost contact
255 if(cmd == null) {
256 throw new IOException("End of stream reached");
257 }
258 // let another method deal with the input
259 handleInput(cmd);
260 } catch (IOException e) {
261 // comms failure, maybe our link is dead.
262 _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e);
263 // stop, and loop round for a reconnect.
264 doRead = false;
265 }
266 }
267 // make sure we're disconnected
268 try {
269 disconnect();
270 } catch (IOException e) {
271 _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e);
272 }
273
274 // comms have failed, so wait a while and reconnect
275 int delayTime = 0;
276 try {
277 delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay"));
278 } catch (NumberFormatException e) {
279 delayTime = DEFAULT_RECONNECT_DELAY;
280 _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds");
281 } catch (PropertyNotFoundException e) {
282 delayTime = DEFAULT_RECONNECT_DELAY;
283 _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds");
284 }
285 try {
286 Thread.sleep(delayTime * 1000);
287 } catch (InterruptedException e) {}
288 }
289 // maybe disconnect here ? - shutdown method not implemented yet
290 //disconnect();
291 }
292
293 /**
294 * Sends a message to the channel.
295 *
296 * @param msg The message to send
297 */
298 public void sendMsg(String msg) {
299 _socketOut.println("PRIVMSG "+_channel+" :"+msg);
300 // wait a second before returning...
301 // this ensures messages can't be sent too fast
302 try {Thread.sleep(1000);} catch (InterruptedException e) {}
303 }
304
305 /**
306 * Sends a message to the channel.
307 *
308 * @param user The user to send to
309 * @param msg The message to send
310 */
311 public void sendPrivMsg(String user, String msg) {
312 _socketOut.println("PRIVMSG "+user+" :"+msg);
313 // wait a second before returning...
314 // this ensures messages can't be sent too fast
315 try {Thread.sleep(1000);} catch (InterruptedException e) {}
316 }
317
318 /**
319 * Sends an action to the channel.
320 *
321 * @param msg the action message
322 */
323 public void sendAction(String msg) {
324 char esc = 001;
325 sendMsg(esc+"ACTION "+msg+esc);
326 // wait a second before returning...
327 // this ensures messages can't be sent too fast
328 try {Thread.sleep(1000);} catch (InterruptedException e) {}
329 }
330
331 /**
332 * Sends a notice to the channel.
333 *
334 * @param msg The notice to send
335 */
336 public void sendNotice(String msg) {
337 _socketOut.println("NOTICE "+_channel+" :"+msg);
338 // wait a second before returning...
339 // this ensures messages can't be sent too fast
340 try {Thread.sleep(1000);} catch (InterruptedException e) {}
341 }
342
343 /**
344 * Connect to the IRC server, log in, and join the channel.
345 *
346 * @throws IOException if the connection fails
347 */
348 public void connect() throws IOException {
349 ConfigurationProxy cp = ConfigurationProxy.getInstance();
350 // setup the socket, reader and writer
351 String server;
352 int port;
353 try {
354 server = cp.getProperty(_name, "Alerter.IRC.IRCServer");
355 port = Integer.parseInt(cp.getProperty(_name, "Alerter.IRC.IRCPort"));
356 } catch (PropertyNotFoundException e) {
357 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
358 throw new IOException("Can't get irc server details due to configuration error");
359 } catch (NumberFormatException e) {
360 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
361 throw new IOException("Can't get irc server details due to malformed configuration");
362 }
363 _socket = new Socket(server, port);
364 _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
365 _socketOut = new PrintWriter(_socket.getOutputStream(), true);
366 //_socketOut.println("PASS");
367 // send USER details
368 String user, comment;
369 try {
370 user = cp.getProperty(_name, "Alerter.IRC.user");
371 comment = cp.getProperty(_name, "Alerter.IRC.comment");
372 } catch (PropertyNotFoundException e) {
373 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
374 throw new IOException("Can't get user details due to configuration error");
375 }
376 _socketOut.println("USER "+user+" 8 * :"+comment);
377 // attempt to get a nick
378 String nickList;
379 try {
380 nickList = cp.getProperty(_name, "Alerter.IRC.nickList");
381 } catch (PropertyNotFoundException e) {
382 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
383 throw new IOException("Can't get nickname due to configuration error");
384 }
385 StringTokenizer st = new StringTokenizer(nickList, ";");
386 boolean ok = false;
387 // try until we exhaust our list
388 while(!ok && st.hasMoreTokens()) {
389 String nick = st.nextToken();
390 _socketOut.println("NICK "+nick);
391 // get a "yes" or "no" response back
392 String response = "";
393 do {
394 response = _socketIn.readLine();
395 if(response==null) {
396 throw new IOException("Communication error whilst logging in");
397 }
398 } while(response.indexOf("001")==-1 && response.indexOf("433")==-1);
399 // see if it was a yes
400 if(response.indexOf("001")!=-1) {
401 // great, we're logged in !
402 ok = true;
403 // store the name we're using
404 _nickname = nick;
405 }
406 else {
407 // log we couldn't get the name
408 _logger.write(this.toString(), Logger.WARNING, "Nickname in use: "+nick);
409 }
410 }
411 if(!ok) {
412 // oh dear, we couldn't get on.
413 throw new IOException("All nicknames in use");
414 }
415 // join the channel
416 try {
417 _channel = cp.getProperty(_name, "Alerter.IRC.channel");
418 } catch (PropertyNotFoundException e) {
419 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
420 throw new IOException("Can't get channel name due to configuration error");
421 }
422 _socketOut.println("JOIN "+_channel);
423 // allow alerts
424 _active = true;
425 }
426
427 /**
428 * Disconnect "nicely" from the IRC server.
429 *
430 * @throws IOException if the disconnection fails
431 */
432 public void disconnect() throws IOException {
433 // stop alerts
434 _active = false;
435 // send proper QUIT
436 _socketOut.println("QUIT : iscreamBot component shutting down...");
437 // close the socket
438 _socketOut.close();
439 _socketIn.close();
440 _socket.close();
441 }
442
443 /**
444 * Overrides the {@link java.lang.Object#toString() Object.toString()}
445 * method to provide clean logging (every class should have this).
446 *
447 * This uses the uk.org.iscream.util.NameFormat class
448 * to format the toString()
449 *
450 * @return the name of this class and its CVS revision
451 */
452 public String toString() {
453 return FormatName.getName(
454 _name,
455 getClass().getName(),
456 REVISION);
457 }
458
459 /**
460 * Deals with incoming lines from the server.
461 *
462 * @param line the line to deal with
463 */
464 private void handleInput(String line) {
465 ConfigurationProxy cp = ConfigurationProxy.getInstance();
466 // if it's a PING...
467 if(line.startsWith("PING")) {
468 // ...send a PONG
469 _socketOut.println("PONG" + line.substring(4));
470 }
471 // see if it's for us
472 else if(getMsg(line).startsWith(_nickname+",") || getMsg(line).startsWith(_nickname+":") || getMsg(line).startsWith(_nickname+" ")) {
473 // setup some String's
474 String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand;
475 String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand;
476 // get the command set
477 try {
478 stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand");
479 startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand");
480 timeSinceLastAlertCommand = cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand");
481 lastAlertCommand = cp.getProperty(_name, "Alerter.IRC.lastAlertCommand");
482 joinCommand = cp.getProperty(_name, "Alerter.IRC.joinCommand");
483 nickChangeCommand = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand");
484 versionCommand = cp.getProperty(_name, "Alerter.IRC.versionCommand");
485 helpCommand = cp.getProperty(_name, "Alerter.IRC.helpCommand");
486 statCommand = cp.getProperty(_name, "Alerter.IRC.statCommand");
487 uptimeCommand = cp.getProperty(_name, "Alerter.IRC.uptimeCommand");
488 } catch (PropertyNotFoundException e) {
489 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
490 // lets bail from handling this line...
491 // ...it's gonna be hard without a command set!
492 return;
493 }
494
495 // we have a message for us
496 String message = getMsg(line).substring(_nickname.length());
497 if(message.indexOf(stopCommand)!=-1) {
498 _active = false;
499 sendMsg(getMsgSender(line)+", alerts have been stopped");
500 }
501 else if(message.indexOf(startCommand)!=-1) {
502 _active = true;
503 sendMsg(getMsgSender(line)+", alerts have been activated");
504 }
505 // this needs to go here if it contains the same words as the lastAlertCommand
506 else if(message.indexOf(timeSinceLastAlertCommand)!=-1) {
507 if(_lastAlertTime != -1) {
508 long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
509 String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
510 sendMsg(getMsgSender(line)+", I last sent an alert "+uptimeText+ " ago");
511 }
512 else {
513 sendMsg(getMsgSender(line)+", I've never sent an alert!");
514 }
515 }
516 else if(message.indexOf(lastAlertCommand)!=-1) {
517 if(_lastAlertTime != -1) {
518 String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
519 sendMsg(getMsgSender(line)+", last alert was at "+date+"; "+_lastAlert);
520 }
521 else {
522 sendMsg(getMsgSender(line)+", I've never sent an alert!");
523 }
524
525 }
526 else if(message.indexOf(joinCommand)!=-1) {
527 String joinCmd = joinCommand;
528 String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
529 int endOfChan = newChan.indexOf(" ");
530 if(endOfChan == -1) {
531 endOfChan = newChan.length();
532 }
533 newChan = newChan.substring(0, endOfChan);
534 if(newChan.equals(_channel)) {
535 sendMsg(getMsgSender(line)+", I'm already on "+newChan+"!");
536 } else {
537 sendMsg(getMsgSender(line)+", okay, I'm off to "+newChan);
538 _socketOut.println("PART "+_channel);
539 _socketOut.println("JOIN "+newChan);
540 _channel = newChan;
541 }
542 }
543 else if(message.indexOf(nickChangeCommand)!=-1) {
544 String nickChangeCmd = nickChangeCommand;
545 String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
546 int endOfNick = newNick.indexOf(" ");
547 if(endOfNick == -1) {
548 endOfNick = newNick.length();
549 }
550 newNick = newNick.substring(0, endOfNick);
551 sendMsg(getMsgSender(line)+", okay, changing my nickname to "+newNick);
552 _socketOut.println("NICK "+newNick);
553 _nickname = newNick;
554 }
555 else if(message.indexOf(versionCommand)!=-1) {
556 sendMsg(getMsgSender(line)+", I am version "+REVISION.substring(11, REVISION.length() -2)+" of the i-scream alerting bot");
557 }
558 else if(message.indexOf(helpCommand)!=-1) {
559 sendPrivMsg(getMsgSender(line), "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
560 sendPrivMsg(getMsgSender(line), "I understand the following commands;");
561 sendPrivMsg(getMsgSender(line), stopCommand);
562 sendPrivMsg(getMsgSender(line), startCommand);
563 sendPrivMsg(getMsgSender(line), lastAlertCommand);
564 sendPrivMsg(getMsgSender(line), joinCommand);
565 sendPrivMsg(getMsgSender(line), nickChangeCommand);
566 sendPrivMsg(getMsgSender(line), statCommand);
567 sendPrivMsg(getMsgSender(line), uptimeCommand);
568 sendPrivMsg(getMsgSender(line), timeSinceLastAlertCommand);
569 sendPrivMsg(getMsgSender(line), helpCommand);
570 }
571 else if(message.indexOf(statCommand)!=-1) {
572 sendMsg(getMsgSender(line)+", I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!");
573 }
574 else if(message.indexOf(uptimeCommand)!=-1) {
575 long uptime = (System.currentTimeMillis() - _startTime) / 1000;
576 String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
577 sendMsg(getMsgSender(line)+", I have been running for "+uptimeText);
578 }
579 else if(message.indexOf("ping")!=-1) {
580 sendMsg("pong");
581 }
582 else if(message.indexOf("do a jibble dance")!=-1) {
583 // little joke :)
584 sendAction("jives to the funky beat shouting \"ii--screeeaaammm\"");
585 }
586 else {
587 String rejectMessage = NOT_CONFIGURED;
588 try {
589 rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage");
590 } catch(PropertyNotFoundException e) {
591 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
592 }
593 sendMsg(getMsgSender(line)+", "+rejectMessage);
594 }
595 }
596 else if(line.indexOf(_nickname)!=-1 && line.indexOf(_channel)!=-1 && line.indexOf("KICK")!=-1) {
597 sendPrivMsg(getMsgSender(line), "That wasn't a nice thing to do...");
598 try {
599 _channel = cp.getProperty(_name, "Alerter.IRC.channel");
600 } catch(PropertyNotFoundException e) {
601 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
602 }
603 _socketOut.println("JOIN "+_channel);
604 }
605 }
606
607 /**
608 * Strips the header from a message line
609 *
610 * @param line the line to strip
611 * @return the message from the line
612 */
613 private String getMsg(String line) {
614 String result = "";
615 if(line.indexOf("PRIVMSG")!=-1) {
616 int firstColon = line.indexOf(":");
617 if(firstColon != -1) {
618 int secondColon = line.indexOf(":", firstColon+1);
619 if(secondColon != -1) {
620 result = line.substring(secondColon+1);
621 }
622 }
623 }
624 return result;
625 }
626
627 /**
628 * Finds out the sender of the message
629 *
630 * @param line the line to look for a sender in
631 * @return the sender
632 */
633 private String getMsgSender(String line) {
634 String result = "";
635 int colon = line.indexOf(":");
636 int excl = line.indexOf("!");
637 if(colon!=-1 && excl!=-1) {
638 result = line.substring(colon+1, excl);
639 }
640 return result;
641 }
642
643 /**
644 * The socket connected to the server
645 */
646 private Socket _socket;
647
648 /**
649 * The writer
650 */
651 private PrintWriter _socketOut;
652
653 /**
654 * The reader
655 */
656 private BufferedReader _socketIn;
657
658 /**
659 * Just a reminder to what channel we're on...
660 * this can't be dynamic :)
661 */
662 private String _channel;
663
664 /**
665 * A reminder of our current nickname...
666 */
667 private String _nickname;
668
669 }
670
671 }