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.26
Committed: Sat Mar 24 03:47:50 2001 UTC (23 years, 1 month ago) by tdb
Branch: MAIN
Changes since 1.25: +10 -6 lines
Log Message:
"last alert" will now display the last alert received... not the last one sent. This
means that alerts sent whilst in "stop alerts" mode will be included.

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