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.27
Committed: Sat Mar 24 18:43:38 2001 UTC (23 years, 2 months ago) by tdb
Branch: MAIN
CVS Tags: PROJECT_COMPLETION
Changes since 1.26: +14 -15 lines
Log Message:
Opps, mistake in that last commit.

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