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.17
Committed: Tue Mar 6 02:33:55 2001 UTC (23 years, 2 months ago) by ajm
Branch: MAIN
Changes since 1.16: +7 -3 lines
Log Message:
Now passes the time since the first alert for a problem occoured.

Also has support for formatting and displaying this information as obtained from the config

File Contents

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